From 4d144360d2ddc446fab7ec2734fd3d9f6a438ebd Mon Sep 17 00:00:00 2001 From: Bertil Varenhorst Date: Wed, 13 Nov 2024 11:10:55 +0100 Subject: [PATCH] project added --- CODE_OF_CONDUCT.md | 74 + CONTRIBUTING.md | 22 + DEPLOYMENT.md | 11 + FEATURES.md | 52 + README.md | 80 +- SECURITY.md | 2 +- __pycache__/app.cpython-311.pyc | Bin 0 -> 22053 bytes pyproject.toml | 69 + src/plantuml_gui/__about__.py | 25 + src/plantuml_gui/__main__.py | 29 + .../__pycache__/__about__.cpython-311.pyc | Bin 0 -> 194 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 172 bytes .../__pycache__/__main__.cpython-311.pyc | Bin 0 -> 339 bytes .../__pycache__/activity.cpython-311.pyc | Bin 0 -> 12785 bytes .../__pycache__/add.cpython-311.pyc | Bin 0 -> 2269 bytes .../__pycache__/app.cpython-311.pyc | Bin 0 -> 37358 bytes .../__pycache__/arrow.cpython-311.pyc | Bin 0 -> 8365 bytes .../__pycache__/classes.cpython-311.pyc | Bin 0 -> 15327 bytes .../__pycache__/connector.cpython-311.pyc | Bin 0 -> 5729 bytes .../__pycache__/ellipse.cpython-311.pyc | Bin 0 -> 3297 bytes .../__pycache__/flaskapp.cpython-311.pyc | Bin 0 -> 269 bytes .../__pycache__/fork.cpython-311.pyc | Bin 0 -> 5047 bytes .../__pycache__/group.cpython-311.pyc | Bin 0 -> 4241 bytes .../__pycache__/highlight.cpython-311.pyc | Bin 0 -> 1199 bytes .../__pycache__/if_statements.cpython-311.pyc | Bin 0 -> 19151 bytes .../__pycache__/merge.cpython-311.pyc | Bin 0 -> 3261 bytes .../__pycache__/note.cpython-311.pyc | Bin 0 -> 4036 bytes .../__pycache__/puml_encoder.cpython-311.pyc | Bin 0 -> 2383 bytes .../__pycache__/render.cpython-311.pyc | Bin 0 -> 1988 bytes .../__pycache__/title.cpython-311.pyc | Bin 0 -> 2811 bytes .../__pycache__/util.cpython-311.pyc | Bin 0 -> 1883 bytes .../__pycache__/whilepoly.cpython-311.pyc | Bin 0 -> 6420 bytes src/plantuml_gui/activity.py | 297 + src/plantuml_gui/add.py | 115 + src/plantuml_gui/app.py | 929 + src/plantuml_gui/arrow.py | 177 + src/plantuml_gui/classes.py | 322 + src/plantuml_gui/connector.py | 131 + src/plantuml_gui/ellipse.py | 95 + src/plantuml_gui/fork.py | 160 + src/plantuml_gui/group.py | 118 + src/plantuml_gui/if_statements.py | 490 + src/plantuml_gui/merge.py | 102 + src/plantuml_gui/note.py | 113 + src/plantuml_gui/puml_encoder.py | 61 + src/plantuml_gui/render.py | 80 + src/plantuml_gui/static/Circles-menu-3.gif | Bin 0 -> 20218 bytes src/plantuml_gui/static/ace.js | 22152 ++++++++++++++++ src/plantuml_gui/static/favicon-32x32.png | Bin 0 -> 1309 bytes src/plantuml_gui/static/mode-plantuml.js | 204 + src/plantuml_gui/static/panzoom.min.js | 1 + src/plantuml_gui/static/script.js | 3529 +++ src/plantuml_gui/static/styles.css | 146 + src/plantuml_gui/static/theme-dracula.js | 21 + src/plantuml_gui/static/theme-one_dark.js | 20 + src/plantuml_gui/templates/index.html | 781 + src/plantuml_gui/title.py | 85 + src/plantuml_gui/util.py | 58 + src/plantuml_gui/whilepoly.py | 185 + .../conftest.cpython-311-pytest-8.2.2.pyc | Bin 0 -> 785 bytes .../test_app.cpython-311-pytest-8.2.2.pyc | Bin 0 -> 621431 bytes ...if_statements.cpython-311-pytest-8.2.2.pyc | Bin 0 -> 1084 bytes tests/conftest.py | 35 + tests/js/ScriptTests.js | 84 + tests/js/SpecRunner.html | 25 + tests/js/lib/jasmine-5.1.2/boot0.js | 65 + tests/js/lib/jasmine-5.1.2/boot1.js | 133 + tests/js/lib/jasmine-5.1.2/jasmine-html.js | 964 + tests/js/lib/jasmine-5.1.2/jasmine.css | 298 + tests/js/lib/jasmine-5.1.2/jasmine.js | 10817 ++++++++ .../js/lib/jasmine-5.1.2/jasmine_favicon.png | Bin 0 -> 1486 bytes tests/test_app.py | 4655 ++++ tests/test_if_statements.py | 32 + 73 files changed, 47841 insertions(+), 3 deletions(-) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 DEPLOYMENT.md create mode 100644 FEATURES.md create mode 100644 __pycache__/app.cpython-311.pyc create mode 100644 pyproject.toml create mode 100644 src/plantuml_gui/__about__.py create mode 100644 src/plantuml_gui/__main__.py create mode 100644 src/plantuml_gui/__pycache__/__about__.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/__init__.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/__main__.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/activity.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/add.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/app.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/arrow.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/classes.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/connector.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/ellipse.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/flaskapp.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/fork.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/group.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/highlight.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/if_statements.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/merge.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/note.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/puml_encoder.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/render.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/title.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/util.cpython-311.pyc create mode 100644 src/plantuml_gui/__pycache__/whilepoly.cpython-311.pyc create mode 100644 src/plantuml_gui/activity.py create mode 100644 src/plantuml_gui/add.py create mode 100644 src/plantuml_gui/app.py create mode 100644 src/plantuml_gui/arrow.py create mode 100644 src/plantuml_gui/classes.py create mode 100644 src/plantuml_gui/connector.py create mode 100644 src/plantuml_gui/ellipse.py create mode 100644 src/plantuml_gui/fork.py create mode 100644 src/plantuml_gui/group.py create mode 100644 src/plantuml_gui/if_statements.py create mode 100644 src/plantuml_gui/merge.py create mode 100644 src/plantuml_gui/note.py create mode 100644 src/plantuml_gui/puml_encoder.py create mode 100644 src/plantuml_gui/render.py create mode 100644 src/plantuml_gui/static/Circles-menu-3.gif create mode 100644 src/plantuml_gui/static/ace.js create mode 100644 src/plantuml_gui/static/favicon-32x32.png create mode 100644 src/plantuml_gui/static/mode-plantuml.js create mode 100644 src/plantuml_gui/static/panzoom.min.js create mode 100644 src/plantuml_gui/static/script.js create mode 100644 src/plantuml_gui/static/styles.css create mode 100644 src/plantuml_gui/static/theme-dracula.js create mode 100644 src/plantuml_gui/static/theme-one_dark.js create mode 100644 src/plantuml_gui/templates/index.html create mode 100644 src/plantuml_gui/title.py create mode 100644 src/plantuml_gui/util.py create mode 100644 src/plantuml_gui/whilepoly.py create mode 100644 tests/__pycache__/conftest.cpython-311-pytest-8.2.2.pyc create mode 100644 tests/__pycache__/test_app.cpython-311-pytest-8.2.2.pyc create mode 100644 tests/__pycache__/test_if_statements.cpython-311-pytest-8.2.2.pyc create mode 100644 tests/conftest.py create mode 100644 tests/js/ScriptTests.js create mode 100644 tests/js/SpecRunner.html create mode 100644 tests/js/lib/jasmine-5.1.2/boot0.js create mode 100644 tests/js/lib/jasmine-5.1.2/boot1.js create mode 100644 tests/js/lib/jasmine-5.1.2/jasmine-html.js create mode 100644 tests/js/lib/jasmine-5.1.2/jasmine.css create mode 100644 tests/js/lib/jasmine-5.1.2/jasmine.js create mode 100644 tests/js/lib/jasmine-5.1.2/jasmine_favicon.png create mode 100644 tests/test_app.py create mode 100644 tests/test_if_statements.py diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..4d6e764 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +bertil.varenhorst@ericsson.com or filip.lange@ericsson.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..928f841 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,22 @@ +## How to contribute to PlantUML Interactive Editor +#### **Did you find a bug?** + +* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/Ericsson/PlantUML-Interactive-Editor/issues). + +* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/Ericsson/PlantUML-Interactive-Editor/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. + +#### **Did you write a patch that fixes a bug?** + +* Open a new GitHub pull request with the patch. + +* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. + +#### **Do you intend to add a new feature or change an existing one?** + +* Suggest your change by opening an [issue](https://github.com/Ericsson/PlantUML-Interactive-Editor/issues/new) on GitHub and tagging it with the label **"enhancement"**. Clearly describe your idea and its benefits to gather feedback from the community. + +* **Do not submit a pull request** until you have received positive feedback and community approval through the issue. + +#### **Do you have questions about the source code?** + +* Ask any question by opening an [issue](https://github.com/Ericsson/PlantUML-Interactive-Editor/issues/new) on GitHub and tagging it with the label **"question"**. diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..8398580 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,11 @@ +# Deploying a new version to https://pdupc-pcsmplantuml.sero.wh.rnd.internal.ericsson.se + +1. Update README.md and FEATURES.md +2. Update `__version__` in `src/plantuml_gui/__about__.py` +3. Commit, push to Gerrit, review and submit +4. `su -l efuncpcsm` +5. Paste password and press Enter +6. `cd /proj/pdupc_webdocs/sites/pdupc-pcsmplantuml/plantuml_gui` +7. `git pull --rebase` +8 `../.venv/bin/pip install -e .` +9. `touch ../wsgi.py` diff --git a/FEATURES.md b/FEATURES.md new file mode 100644 index 0000000..9cce8d1 --- /dev/null +++ b/FEATURES.md @@ -0,0 +1,52 @@ +# PlantUML Activity Diagrams + +PlantUML Activity Diagram documentation: [Activity Diagram (New Syntax) on plantuml.com](https://plantuml.com/activity-diagram-beta) + + +# Activity diagram features + +## Supported Features +### General Features +- Automatic Indentation +- Syntax Highlighting +- Copy / Paste buttons to replace from or copy to clipboard +- Undo / Redo using buttons or CTRL + X / CTRL + Y +- Syntax and server error popups + + + +### Creating and editing + +- Activities + - Colored + - Embedded Link +- If statements + - Embedded Link +- While statements +- Repeat While Statements +- Fork statements +- Start/Stop/End +- Connectors +- Notes +- Titles +- Detach elements +- Break elements +- Switch Statements + +## Partially Supported Features + +- Groups & Partitions + - Can be deleted and edited, but cannot be created or moved interactively +- Break is currently only supported for Activities, but could easily be added for more types. +- Arrow labels and switch cases are fully supported as long as they are not identical. +- Split Processing actions are editable and removable but cannot interactively created or deleted. + +## Unsupported Features + +Unsupported in this context means activity diagram features that cannot be interacted with in the diagram, +but adding them to the PlantUML code should still work. + +- Goto and Label Processing +- Swimlanes +- SDL (Specification and Description Language) +- Lines without arrows diff --git a/README.md b/README.md index 3225177..e6f3766 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,78 @@ -# PlantUML-Interactive-Editor -A graphical editor for PlantUML diagrams +# plantuml-interactive-editor + +Welcome to PlantUML Interactive Editor! This application is designed to provide an intuitive and interactive environment for editing PlantUML code and generating diagrams in real-time. Whether you're creating a huge activity diagram or a single while loop, our tool streamlines the process, allowing you to focus on design rather than syntax. + +With PlantUML Interactive Editor, you can: + +- Edit PlantUML activity diagrams by directly interacting with the diagram +- Also change the diagram by editing the PlantUML code, just like other PlantUML editors, allowing you to choose code or interactive editing depending on what is easier in the moment +- Instantly preview diagrams as you type +- Easily share the created diagram + +## Usage + +- Right-click on elements to open context menu +- Double-click on elements to edit the text +- Submit edited text with the button or using CTRL + ENTER +- Left-click and drag in the diagram to pan +- Use mouse wheel or pinch on touch pad to zoom +- Delete the text of a title, note or group/partition to delete the element +- To share a diagram, just copy the URL in the address bar +- Hover over elements to see the corresponding line highlighted in the editor +- Clicking on a line with an activity in the editor highlights the corresponding activity in the diagram + +## Detailed list of supported and unsupported features + +[FEATURES.md](./FEATURES.md) + +## Pre-requisites + +- Python 3.11 +- PlantUML, https://plantuml.com/starting +- The path to the PlantUML must be set in an .env file, see .env.example + + +## Installing and running the server + +In the target python environment: + +``` +pip install . +python -m plantuml_gui +``` + + +## Development + +### Pre-requisites + +- hatch + +### Setup + +Commands to be run in the hatch shell, started by typing `hatch shell`. + +``` +pre-commit install -t pre-commit -t pre-push +``` + +### Running tests + +#### Python + +##### Without coverage + +``` +pytest +``` + +##### With coverage + +``` +python -m pytest --cov --cov-report=html +``` + +#### Javascript + +1. Run `python -m http.server` and open the server in a browser +2. In the browser, open tests/js/SpecRunner.html diff --git a/SECURITY.md b/SECURITY.md index 36f0da7..e08fd6a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -12,4 +12,4 @@ Only the latest release version of _PlantUML-Interactive-Editor_ is supported by ## Reporting a Vulnerability If you find a vulnerability in _PlantUML-Interactive-Editor_, please report it as a security vulnerability on GitHub -https://github.com/Ericsson/PlantUML-Interactive-Editor/new/main +https://github.com/Ericsson/PlantUML-Interactive-Editor/security/advisories/new diff --git a/__pycache__/app.cpython-311.pyc b/__pycache__/app.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fa30a2e59c70ae8f613430d7a504d089e3efff6 GIT binary patch literal 22053 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 F{~xcQ>aG9) literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..978ba87 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,69 @@ +[build-system] +build-backend = "hatchling.build" +requires = ["hatchling"] + +[project] +authors = [ + { name = "Bertil Varenhorst", email = "bertil.varenhorst@ericsson.com" }, +] +classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] +dependencies = [ + "flask==3.0.3", + "pytest==8.2.2", + "pyquery==2.0.0", + "ruff==0.4.8", + "pre-commit==3.7.1", + "mypy==1.10.0", + "pytest-cov==5.0.0", + "python-dotenv==1.0.1", + "loguru==0.7.2", +] +description = '' +dynamic = ["version"] +keywords = [] +license = "MIT" +name = "plantuml-gui" +readme = "README.md" +requires-python = ">=3.8" + +[project.urls] +Documentation = "https://github.com/Bertil Varenhorst/plantuml-gui#readme" +Issues = "https://github.com/Bertil Varenhorst/plantuml-gui/issues" +Source = "https://github.com/Bertil Varenhorst/plantuml-gui" + +[tool.hatch.version] +path = "src/plantuml_gui/__about__.py" + +[tool.hatch.envs.types] +extra-dependencies = ["mypy>=1.0.0"] +[tool.hatch.envs.types.scripts] +check = "mypy --install-types --non-interactive {args:src/plantuml_gui tests}" + +[tool.hatch.build.targets.wheel] +packages = ["src/plantuml_gui"] + +[tool.coverage.run] +branch = false +omit = ["src/plantuml_gui/__about__.py"] +parallel = true + +[tool.coverage.paths] +plantuml_gui = ["src/plantuml_gui"] +tests = ["tests"] + +[tool.coverage.report] +exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"] + +[tool.ruff.lint] +ignore = ["E501"] +select = ["E", "F", "I", "N"] diff --git a/src/plantuml_gui/__about__.py b/src/plantuml_gui/__about__.py new file mode 100644 index 0000000..89c612d --- /dev/null +++ b/src/plantuml_gui/__about__.py @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +__version__ = "0.24" diff --git a/src/plantuml_gui/__main__.py b/src/plantuml_gui/__main__.py new file mode 100644 index 0000000..d572661 --- /dev/null +++ b/src/plantuml_gui/__main__.py @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +from .app import app + +if __name__ == "__main__": + app.run(debug=True) diff --git a/src/plantuml_gui/__pycache__/__about__.cpython-311.pyc b/src/plantuml_gui/__pycache__/__about__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9de1b3c9ddbeaa871016a03737bef9bb0f4a98b4 GIT binary patch literal 194 zcmZ3^%ge<81pLap>1`|w439w^7+{1lK7*7@XGmd)Vn|^OX3%7+VlmJ&GV#-7yu}?K zUzS=_oSB~&AHR~}Gf2ZP2mOrv+*JM4vc#gK)FS3d literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/__init__.cpython-311.pyc b/src/plantuml_gui/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..055020c2776e27d425ff70002e9eb2e1003d59cd GIT binary patch literal 172 zcmZ3^%ge<81O{)5)2e{^MY(U0zh7^Wi22Do4l?+8pK>lZtujQreG z{nWC=qNLO!{eq(WtkmQZ{eqmtypqz~9NqNNO#R}bWVk>)NFY8wGcU6wK3=b&@)w6q iZhlH>PO4oIE6_ZUjm7*x;sY}yBjX1K7*WIw6axU3S1QB+ literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/__main__.cpython-311.pyc b/src/plantuml_gui/__pycache__/__main__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ebfce2dee1b0487cd970056082896c342bc4e1ba GIT binary patch literal 339 zcmZ3^%ge<81dk8!r0-?{(jX2DFhd!iU4V?~3@HpLj5!Rs3{eb>45^GMOxa8z`66K^ zhE#?uFdM{AVP3pvlK=n! literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/activity.cpython-311.pyc b/src/plantuml_gui/__pycache__/activity.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..508f02edaa1882f652dff7664eb7c87f10d9a2c8 GIT binary patch literal 12785 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

y{eMTgg9)xpM<}+B* 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 literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/arrow.cpython-311.pyc b/src/plantuml_gui/__pycache__/arrow.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51dc597f6f5811d20a225a7c09c00123f4b9f95e GIT binary patch literal 8365 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 literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/connector.cpython-311.pyc b/src/plantuml_gui/__pycache__/connector.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99f5a33ec6bd1160984cd40b0727b7f23c68b925 GIT binary patch literal 5729 zcmc&&U2NOd6}}WnQ9qPT`A?P;yY}qZmc7Pp+~#j-+Qe~_xQ$!S?Y0_O4Mn7FCA6em zQfcE+m4P7$mmwSakVi=%V1k$d1AFS5hHOBQ4eQROWZIHt z)EF@IN;JZu%FTzRTNx=3P@Rcy@k_rraV2)FZkiuf$9Ij3c-;I+}5a?vI%% zpXdRJI?)TX9^?ez^T79uKHxNn)FcYkeM5SUhbXf(8WW`tr{goz5jh&UCW+IMEX_+X z723GBZ%!m7eDfO$ghLLK8 zhVH}DLz|OwIFRNLsVXk5(dW=FAEua!zD$)Aqmr4*MXPRQHI{7ZCp$Jrq!&Os#j5O9 zFNChzV`yyE_)d+M_$29dqyAfkArQX=cL2iQ>& zot0}4xpMCwjmna}-BncoHcNBPqOV-{VZMjfoTR04JPO_UN+Zg5kDHuS(&RV)`R=># z7QClV4V4+EG3jkGiLg3nGBZ(GUf{Z4unHJ}Gk)c&6j4o|Dt)MK8Hy5en~t}yo3x^0 zOy10N&EWX_v~pwCWMUv_(rS3tq-Q17bcPcNDJEj_Iw2Qy0U2k6SdwB=qUkEHLcwk_ zj<@`14P`&T8ca7C*IIz$0?E#DdG^}STzp;{l5T|Y6$uX|aGb1nC?SVqYI0tFel{5$ zQgFm(OcO>V9*cog93M{HGy||CHEu0Qjw-6%hC5)MF8C>b1+s_?U;X0i8!k_J_!f7E zTRgK_FQl(81#fA0w8aZX-L!O$OiiciPeD`3C}$=KkqKfwoNFvb-Xv zy+%t{wmW+y+pV|suD0h}j;6gEE$#4-{Z+nYSZ^6ldpDa7+9U7;;aju1z@)1g??S=U%glmUd{fvplpN z<}w&jicT>a%G&M@UF}lkMBuI(tudm5xacR2R{#UEwh`N%pi=FoV9d<8$zGA<_y?G{ z-80G)qRmDywdoSB+4?g*NCUZ3&jXiLJnS)W7JgD4OxLH@m!7>b*tfGc7 z*f5x_$y(hNvL7Xj!|hNt1rdZ*Y1*C)KM(BV@KeTsETRp6PxhVFQ;#~H1oHj~-9Pcs z*y3n<*x-e`Z{2Z{;^OAqDDl%Xa8Q6sYi3Cui$_Sg;bEf}& zO|gBi={=1}xiwmJmRB>OLG*!D!uq$P${u^hRqaGO>AT%abX~J_g8kNrK6r1Yw#1sh z)~uqtye4+LKZRn{oqxlcJ(^qd%s9XaJ>yo`ct-UIWbun?jbU6DVCgF?6Y?q%xel-e z7|;6_M>qJM?B%u7dH$@D2P& zmCI?5;cZ^`b{4#y*~Yy0knTNHM6^3#@Ga|nSAhrg&-1-H-&>>!ckt!)!PAAo(~p$= z;D|mrVsVR3T{u(_ z4rL>GVL%rKiZtO4omd|_TNpa~csM_FULQJdabeV8#}aEawr0*38oP6i-A3SG=H2YX zXYau)tp|=20!P-4<^w17z{w(VH1`=D-PzGElVOdf;L_-~hNpgUguG05`ecr2w$QC2@Ytw^wor|(2tK<} zZT}&MuE4db{O=kV0ocRWBrt7zQ_871_l0o;3(4Adl^Phgm*j4Y;*J6f9gVW5Kcb=80h!rcuOzy+viZO>d^zL^s`AyrxO-}0o{ zVi0+S%C~|Sv%kAt+Rh~#VB7vrx#W%jSUZobOi60HbhWEu2Ec%M1;v6ITidz8*{nk* z^aQxtK8BH#d~MgQkf0lut-yTSK>h^lyN4i4h5y~9QiUnH;y%DS zl-#F53GgeG6d>VVr+?959Y5#=xVqU!`${E#NTr;(8SuY0u2-Qo$u3KW6STlx?(aVw zx?D<%LadeYSmlZg;ljZ+pn5wJ3S^IPtF67C=g;ZkAtQtcgb>5U)-%Zq;tMx59o2Z5&vdLkFI#@(DsS8jokEJIgxruk4T=+U( oMDScqUJ|IrBC<&@M@FzolamTQiWCw2Rg(sLn(YZm&Dw$g0!%BpOaK4? literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/ellipse.cpython-311.pyc b/src/plantuml_gui/__pycache__/ellipse.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65a0d69dc4647783c4a0cf1751a7d20d34f3e9e8 GIT binary patch literal 3297 zcmbVO-A@}=7Qf^9^jF8k*nlw`5VVO&HZ-J3gwi5G1KLUn{fMg7qD08`Ou%9MkvrqG zp<{`MRy(9fH5FB;lm|bhR9UHc=pWGj2V-lbj7FkDyXr&UTxD0P^3d$LgFRq3XwhDc z59gkH?z!h4&iS4DZ8#i2FvOoii$8}E`WKnBhOfyyn+4`Rl8{6xC`Zjx6ybe}FGtTa zz|#_=usLp?%klF(%-PS-ydZHv{Sq$+Zu%tQGiE+0`LCf=;3+YhqMY7rUXnjqDBN7g zDA~-iEG@{2oRjk!tnu&NeZM5*yH6>i>vQ-SMadRb*%7YYS$un`lwZz}mDb>aq2c%J z1Mv7hT0~_^JFu>!hcJ8ONy}&3G4}pE;P0PH_>#O$AR%R51$mfimz zo^87e-*Fb-c9+goK^G3Wi*U$Ygbw?D-LG>^PpI|p!Q-=sddxQU2HQ2NoOk5|jKsB{ zB=O!U|J11}$%sD;t!rPTbBpL7!b6DBqgIx8WC;biurBBnGQ8BzhNLrIR%^g_jcVkg*~ zx~9Ad_~7`7UDp98?6I!efhj1e1Pung@qBV@sgRS$^JV;J~pE?Cqxr*$txH`Kd1dMZQ{5ron zwJS!9+m*gGeO+JuiyiH6pk8*C`qiZdzjLhH4n=>ZebK z@uBK)b-X%k#b4VVsKqZB!JYU3Osb#O;^(aRIU~5+-GBeeS64RXHs=igZYb8E1%IEN z7_Lq|IBg|Hx3l%cMKf{nm)^wI#5dswVdI^h-yXJ-XC4pK zl9R^Oog_?7Z{MgTFImY;pdu!#!Mb?L6i?ZaUiSdjAKQ)eZ4GUPtjJ(>wjLQZBct}7 zNAKW%6NzZ6M`QuBK`pJ1>~m6bQ=y(y01O=%Z!ey9zqg(tz z16&I!`;NSyAzr?P^1ZIF&^61@JN|c69)BURRS%)P5CGE|MZg?x&IJ}s^Z^wLn*1H@ zFsfE4I;^UtF@OZ%!gDH+;xLJ<6HtpvR#UQhS#@Yd&J#79ErOhAs~=|xKHVy0^CAccx!_6-2ux@{L*i506*Bo}wFhl-0fs7)j^9!y>fU{e0fsYeM;u4t9 zWB?ntXSYxfUx39a_^H1DftnA6S7&xYBh~AVCu^Z;D>S`2V}~MNzI*T8T4=xu4Zt)Y zemz`~H%2!{>w%;hNZMj|<->K|VC-1m{rRuvH?D79Hv+D7>-~+tS>o|0;%HqQ-M&y0 z$1QQZfjED{7Wu$Vfz+!Z^c-#qXSQ(C+pFZ zW@@SyowlOW4dm-e*u4W=@>a<1?z4q(rE4uWaPp&NwJG$g$bmPi(=vQTiydI8Jk%A8I zk^i$IWAJ`p?jWc)wpnk4Yt3 zmvS;rYcf7X6iyQbKR!eFF+yGk;;$ax?tX_~F%9o);_F6g=o zdr#9=v$_OY*N8d+t0N`uPS0R9R_i%o^_*xRo=v!9EoiWYcI3=u-OM!y&YL3nUYf4v z(1aN!A0%gG<~KVSshnM#Fqv*Q^X5_^r>oVeNn<`!(z41PTJXiYx5*0|lT5@S5Qonw zX;qa~_etVDINUbv)~!Ql^BN|B_ztN{gdTFM9aZxf{ARZ(DHWg@y zUP0wA4x8Nkl+v73yCRT(KyE4qx%vY$BO~Jt2Js81=m8sdLu!XdV}47112YH~aR3zq E035bP6951J literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/fork.cpython-311.pyc b/src/plantuml_gui/__pycache__/fork.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27f36cbdfbe29281af76ceb3455f1f06bd8ecccd GIT binary patch literal 5047 zcmcgwU2GHC6`t|@*`7&k=ZE|_Bq4;w&W24FmhzXV`uL; zh*;yTRe?H5BjF&bxK%6pp`xMiz)Ph*wr~5;2ivl=qmd#-s``M`2dlhPJd~b$$98NJ zP<9`7};d(ORc=X~efbH;yaYI0I=t^HjJZM0I1q~-?IAsxL z?3EG?qaH)= zX>}xNvr(E&rW-~XjTxtY{_7+g$7_`@jm_FBd~GY+>Sx#YWM+ZPLczLyu=QH-B)s$T zr8P=9jRUaR`?g&$WiA2QNiOcF%%Z4yuEJ|v!}`2NXDjYgQ6g>JwcFMB5cQ0I4Eudr zebf4NXli|3qX95s%488hWAbxKI5Zc3ffs-Azt^u{C+z1(N3iHTz{kgK&tt8oxN zqgXhqe!FSA_JzsKMq+`u$%GUjY8)ZFebYXRVv92b-@ww65|t5N+Akn%Ad?Sb{8f{d zO%6XqML1kcJLbtGi-X6iSij%8UY#46i!CZ6%AEkZt)P)56vNvZS&9Ur@#V$HvCwjO zL`A{szziOkjiH4PmR8Ib*i1Y|9vlg)aXgHAVDVnK)n`Cbl+ol$O>Wzr`iWIxT}X}X zNKN{fKD&Bp{ZeYeaJQEzp8JHhsLpPS?sVH`=flp7FYh^A^c>EMgGF(0i!O-%oaogr3_FBPo93mq125;Ef?WMn!C z0g+^6TatyFlbIy1v31Vd5JU@jNeZPjN@I5&G%098wi;+erA!_}`?NZcv}<;mEeA=9 zM$2587c^ewG!}NlC;9b{u_Vy}E>CMb5^Lt*;NTKXI2d45ux6_~@b>abUYOBLwD5ea zZ!1d}Sf7a=-kpcIn&+_Pb=m#A|MQe))0kNs1czhl1q8xtgsnVjvczGqm4EWe4*uMa z`>!0BTU1a;fiPhrO4Q^a1j0*xhsj04QAIVmU~Cx(c15`*~{fi_A2h zwKNr_*9SH1QtTA*CWPH6(K3rc>o0GK_>J=d10tEB6ey}vxLWpiRDke0<*7YA|q6Kj{Ck}5*o?Od& zdFg0TI+~M?0(SaDiKC=rC7zNzsVkN}*(Eq=gEjed_u%<6JaCh$J7Q?fR-cE!mejtE z6EiryUSCGhMG%zxmi-#svI0Rq6Phmr<(->Tc-K2Q-e zS?v06Tzde_P9>cE{bvKgcsLe4mvCYz`U9arIEqfed_PA}N2h_e;O61{RZtwA!6RpZ z?13s?WX4+LJZQwQRuss$LX~y=>dG&#q&qXCdDd5CeL2==>^1gH!2UOE=DP=5<9ADj zy}_(67v##>$;&o~VSW^L9pz-Y!tWpECvc`7g4t*QlRbe&gIM$qR7HYnJQfN?6f_3P zD{!mJK)w~Mf~0?b?-ySSJq4jBFZ33L-t1687|sd9M)wh&)8|%Qx@)I<&|=>f%C*HE zKGR5X_d8%VDL6x+eeGcCQ%UlIl*sq41hhARcSx=WMOe)Pba(q*t1+LL(ynUz?TS=%nTHm`?E6SAox1O zD^loV4SL_q;<&)B7S6c5t$b-(MQs-@b4kI>fgX2E_rPysRVw|!K~+M6>kuBr5r!_{QTr~eCIclcE`6cKHR&)K z!b&7V4g;s!fdJm*va+LOWk*u#9!HlzzK#|D0t7&Jx>6SmsToouR4VkUBQ;j8U+bJ< z$0_Q8wIliwT)bXizx&|JwJ-CozM`uS%2?5r{{Gr|{d~1-uo68oASF&VQJ+H{Lcr%?uFby zOd%KvsH&op4`cEfjNmUJ%7h9Zz7_B%C`mt7Ho~O>!kLAfLnIuCd7e-t!4vDfvX3st zH52wLku;5@MX;2isVD=B pm7!@QZ4OWlmMCbp+P3DN^%C|gFKHS{CyN)TlIAwteUlLD{vYx)JN5to literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/group.cpython-311.pyc b/src/plantuml_gui/__pycache__/group.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac8377e9e0792dafb13080613a261fa0c77452dc GIT binary patch literal 4241 zcmcgvO>7&-6`uWHa!D^4S&}VL5>?qr)(#~rv17}U-NcRxSP~?=ZqQbBD=6Bv%!s0x z;mVfiC7={3Kp6r|6)sGm2*8s=!#()uqkG7GNh}~@fdB$}@{NFjqVS>an_Y^3vXI7y zEV#38-@JJe_tc`uE%+wZ)&xFFAJD zZ_uic@`E4R;0>y(r70Arwq^|)HcOnw#MEXJ^ZKn9|9$=XbxOQAHbmYo+FVj!k7=oJ zQpYPx+WCP~&Nb@3M(#va{nq*6!84;T$Q~(-O<(=WW}w$*7ByWFK)$_bA6ke-7nU?t zi)qVRT(`M}l~s69s?CvEl9(s+i$tCS`=kJ`VfT79Hgs!cSsT)B&*LvNJe0sIUup~b zP$D)T*H@Qgk;T>MP!ccH6mvweh*wq&nJ>InZ7~0@vX*8TJP`m_4?+wzURgTSr#k9(gd55zB4e zxy!j<=Pp0EkUw8+i-1Owv((nv`=j?pvv-P8pC$E~1LGxW+?2-4zF_v+=0s+^EO+KE zmSn}06__T={iiGiHl|*}qR^YcX~O0baNdA(O4DfoV478#GzU-KV4FbK0@x+u6yfg; zWUzGzr3FLC*Ff!nMW-iNKZJiZ^IEeDDl!zZMQi=gtNs!@MD?qPwyrkv-K_9s(=6Dl@QRCf?(WBkP6z z-&^nha}rO#H)w;oNoePtR_GT%w$~W^Acum#vN;E@wkI)<^{5_QiDM6R{wC$o;%eCK zqtZY#CZ@#+H*qv!dy_g?C+|e{TZ+f#V$ryk#1MDU9{Qbr+v8FJ9NAnnu4-$xFB(rq zRc+4o%f+ok?q=;%pP@UR&G18G37|RUiohWS#A5ogA*lh$n=KF^5N{~TSkU0w;z2t``6j+ z8xL+|yt`8Sjub9Q;e2ONQY=XUB6>OtvpbPXrO2h{qs7RK6`66A;2=lcOJ!2!-hr%C z?i`I&o34OgH~X$!hyEJBhbsoO7gM% zABys@B@b5wqW2>IJ5>TpofX7-I&%|+lcmrZGj!%z*Yjj)WWpSosIMl}pY_1}wQ0+u zKO5ZiX1K}_k^;?}P&tKsojH#w^_y%zZijuuhoDhte1yn^x;BqBeHF(S8nQ?|e_z&SGfkO@GU!JTSe`iff{abGI#>5$0OXtdoiM}!^cjO|2`Y_itGQ6A=3s$C z5PtRwftnQ&R9a=4=Te#a4A;?t;~*R!Jnn$GxP4<9ii~{24%C;}ypKR)VcI#MS5(*` zs(Vn>U5CvI57bP=qPojSl6@SO;2w~Gyrc_|q}4t8YkTET&hiLwyf5ke&)l>2Q_IBp5YD~REysIq)? zNA4@heR(E-tth`|$?p{?OFm(WCw6OHQUSV#EP3cDbbYuZpDW4dp0z!jFUp@-@+Z%O zmV9|fzEYB}6y-@vo;1Zt2l@+**dVe~p&kAy2xor;;an5KS*XUE7~mkBhcwBkdli${ zU=98Pq)|gq=@b;W7f?Jxbai~@-;92y1^phCHJHDnZ)q}m3_dUJQ8}Vf`EP-1u1-CG zA;3l0hk8Zo)NeL&(MMuMig$n#PI+rNB%GqdcVAIng~KPo!l{EAkjQ?y5{)Y&j*z|@ z1Z~y77;KXJ;pf%VXcd3>V=$Q|W{;uyQRwvy7FM5X#hx*%XKXKF2vP?BIkk}@ zk@KYK>m^SmH@_V*ePQ_RN`Y**CH3q`@0Xd%Eom5F={Z;KKUFxE@5}dP#*{buv8!uzRU1=X5x zFZGKGsx_A=k(#0ki{{4@*5G6XkvH?|bys*p+u@@^6;x|_dGcCo-eHOJ{f+ZQ{{uZ~ BhZ6t* literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/highlight.cpython-311.pyc b/src/plantuml_gui/__pycache__/highlight.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..01c327ce3438b8b07a0d382e36b3a281c946f5fe GIT binary patch literal 1199 zcmZ`(%}X0W6rcUdZZxSeDzP@LiU}A{qtrrB2^B0hP*RIGp@cNM`Ow6TlTF$U8}QIV zgdCy>rc!VZ#ag`fQ0hOCNDg7aA`}na1TV!y?MzJ4CgPjjx9|Pl$GmwnvtNwHHh^g4 z(NPdk0Q|yAoyeMS*^7i7zyqF$f*7$x5`ZV)gC&Zm7J-94$8HWnV;AICNdd}%dK|`e z=tX(KI{o9!6~Ht@QnbSaVRdMXlFd?UzL)v z=x8VzaVB7(0r)TwM$3-krH{pB%@TxS%qQ>>NrXO0^!Wo)Bo&clh_?ry(8N!~*1-wa zmVLaz=GgVwQ-d)(n+*~q#o33qFcJ9Q|SD4(Cx8>~filtwr zRI^HLXVi4bGJ%j@>z;mB|FQj3d)8Cw=q+^SlyXP^1)z*J)pOwg{Gx1|{H{B;xr;V; z#cJR4?s`ksk+O9pudj4jRl3+^Lr>+wQ2r$zzfU{5v&l=eLg01K8XK3_@Rf&n;n08? xZo^jq`>>4v8&}*+OyrYM!3`fG72o#6IARwRK@b%%SZsbSXfuI&bDLE&{sri^73Kf{ literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/if_statements.cpython-311.pyc b/src/plantuml_gui/__pycache__/if_statements.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5bee77a0ab8b2b7fa89307aa24495a8b670cda1 GIT binary patch literal 19151 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 b8&xAg4%<^B9V~{{h_}|4TfK@dGUfj_`a`*7 literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/merge.cpython-311.pyc b/src/plantuml_gui/__pycache__/merge.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..008c76a144c381a907dca051699d2af856760ebf GIT binary patch literal 3261 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 VhtpV^CekzpCkZ&&@c-Su{tZ8-761SM literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/render.cpython-311.pyc b/src/plantuml_gui/__pycache__/render.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b362f8219ce42695835e6ea9a2e72ca7ff9180a1 GIT binary patch literal 1988 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 literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/util.cpython-311.pyc b/src/plantuml_gui/__pycache__/util.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09a94905f0b42adeccbc39580af2d63a8ab3b1c7 GIT binary patch literal 1883 zcmcgt&1)M+6rb5IZLD3(wruLuX-VSRcvZ(HC20*V_Mvqs#h3)=WN3x5JCavgt;Fog zwabd?(1ThGb)h9bw4hV!k`($c^ib$g5(`}h3Wbt`ZvxYcPn{Wk*h=Z8ls7Z}vM~9|4r!|4GZessiu_8}v!)31@3mcnmCHK?fA!4lD{g0tA+TL?m48TLVi( zQdY90Po*8%l97-VEQQL7725`eddln!fyD2ZoP!Mq6_M)`b$c)O&Thpm5Y5}mnM`tQ z&nF5-eg(UxFbEESNM-h(tx!hMz^V{{cfkjfRWT4O;bR>DsuA-8mjD*HKc-k+csLaZ z6oy*w{mv>7=w2byL z_z}X(Wn5x$mdj4Y^(#f^R<2?%d)OQ((o9kDZD*-`K&EZiLi_1bHr-n<-CGck()ict z>#b5&1CgfJHloSv9U#gRoYmDx)sMzM8>!1tJbAc&?6%{J&G=%Zjn_9iBG7LA9v`c1 zo>6yzHOfr3(>}$hz;pN^O~PZ~Uz!U*elU7MrKf|D1qJhbPkvI+8`?QYw zRNyw4p3gS8?2#WtYAAi%%U??zj#-5{db;!#FXObNi|Wg>y>Y5aLpJgyn*p+_>?iy7 z09eXUCgAYemi&b7;^|~b=b~}FSl%?$6Ib7_v9P2yGPT--T z5z#>|(S5Y<_iNuHl7CP}_zE*)o|s?-W2Za~GKf zJNwbD?I1i$RddX8lQu{V{1zLjuY~a%t=P>lcC+!I9h+~)=6`tYWdGSGt<-v$TK{>q zo!V-qwxYyjy%OnDhv`tC{Z%*Gx^Xhw(w9Pgsdg_)jMv`lvgAydG)``W$;B|aRNIVR z7(dr1j&6T#9?ySS_^Qyj-O}emeXawboZ_qwBX#VH#OH}`<>PGQ!8dkmdLf)%XsJf1 z8cqIC3>_fIS2?5hajhP-<1@|pOq85Hx_fk&T_e~2RKRHRe_5%c0Bx*(2PbG&w~QE> z>|dhn&RjB`jOQVbR|IBVK_vc*5NT=f>yn?y_kg?e13OpWE|#o{gVyi@wP*L3$NmZ( w5klB`1HiYM;LX239YulTCwG2SpRIKO6@M}L=S4O#1;>t8I)Hxuk#Q`40qF~#%K!iX literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/__pycache__/whilepoly.cpython-311.pyc b/src/plantuml_gui/__pycache__/whilepoly.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9776b83571ea3555e99710234462b8fcc8ac4850 GIT binary patch literal 6420 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 literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/activity.py b/src/plantuml_gui/activity.py new file mode 100644 index 0000000..ef43220 --- /dev/null +++ b/src/plantuml_gui/activity.py @@ -0,0 +1,297 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import re + +from pyquery import PyQuery as Pq # pragma: no cover + +from .classes import RectElement, SvgChunk, TextElement + + +def index_of_clicked_activity(svg, clickedelement): + count = 0 + d = Pq(svg) + + rects = d("rect") + for rect in rects: + if ( + float(rect.get("height")) > 6 + and rect.get("style") == "stroke:#181818;stroke-width:0.5;" + ): # check that the rect is an activity and not a fork bar + rect = Pq(rect) # rect svg is self closing, so no is in this. + rect_svg = str(rect) + rect_svg = rect_svg[:-2] + ">" + rect_obj = RectElement.from_svg(rect_svg) + count += 1 + if rect_obj == clickedelement: + break + return count + + +def activity_indices(lines, i) -> list[int]: + return _activity_indices(lines, i)[0] + + +def _activity_indices(lines, i) -> tuple[list[int], int]: + index = i + indices = [] + backward = -1 + + while index < len(lines): + line = lines[index] + clean_line = line.strip() + + if ( + clean_line.startswith("#") and not clean_line.endswith(")") + ) or clean_line.startswith(":"): + indices.append(index) + + if clean_line.startswith("backward"): + backward = index + + if clean_line == "repeat": + rlist, index = _activity_indices(lines, index + 1) + indices += rlist + if clean_line.startswith("repeat while") or clean_line.startswith( + "repeatwhile" + ): + indices.append(backward) + return indices, index + index += 1 + return indices, index + + +def find_activity_start(lines, count) -> int: + indices = activity_indices(lines, 0) + start = -1 + for index in indices: + if count == 1: + start = index + break + count -= 1 + return start + + +def find_activity_end(lines, start) -> int: + index = start + + while index < len(lines): + if lines[index].endswith(";"): + end = index + break + + index += 1 + + return end + + +def find_text_bounds(lines, count): + start = find_activity_start(lines, count) + end = find_activity_end(lines, start) + return start, end + + +def check_backward(puml, svg, clickedelement): + lines = puml.splitlines() + count = index_of_clicked_activity(svg, clickedelement) + start = find_activity_start(lines, count) + return lines[start].strip() + + +def edit_activity(puml, svg, clickedelement, text): + lines = puml.splitlines() + count = index_of_clicked_activity(svg, clickedelement) + start = find_activity_start(lines, count) + end = find_activity_end(lines, start) + if text == "": + return delete_activity(puml, svg, clickedelement) + + activity_lines = lines[start : end + 1] + activity_text = "\n".join(activity_lines) + activity_text = re.sub(r"(?<=:).*?(?=;)", text, activity_text, flags=re.DOTALL) + + activity_lines = activity_text.splitlines() + lines[start : end + 1] = activity_lines + + return "\n".join(lines) + + +def delete_activity(puml, svg, clickedelement): + lines = puml.splitlines() + start, end = find_full_bounds(puml, svg, clickedelement) + del lines[start : end + 1] + + return "\n".join(lines) + + +def add_note_activity(puml, svg, clickedelement): + lines = puml.splitlines() + count = index_of_clicked_activity(svg, clickedelement) + start = find_activity_start(lines, count) + end = find_activity_end(lines, start) + if not lines[end + 1].startswith("note"): + lines.insert(end + 1, "end note") + lines.insert(end + 1, "note") + lines.insert(end + 1, "note right") + return "\n".join(lines) + + +def detach_activity(puml, svg, clickedelement): + lines = puml.splitlines() + count = index_of_clicked_activity(svg, clickedelement) + start = find_activity_start(lines, count) + end = find_activity_end(lines, start) + if lines[end + 1].strip().startswith("note"): + while lines[end] != "end note": + end += 1 + if lines[end + 1].strip() == "break": + lines[end + 1] = "detach" + elif lines[end + 1].strip() == "detach": + del lines[end + 1] + else: + lines.insert(end + 1, "detach") + return "\n".join(lines) + + +def break_activity(puml, svg, clickedelement): + lines = puml.splitlines() + count = index_of_clicked_activity(svg, clickedelement) + start = find_activity_start(lines, count) + end = find_activity_end(lines, start) + if lines[end + 1].strip().startswith("note"): + while lines[end] != "end note": + end += 1 + if lines[end + 1].strip() == "detach": + lines[end + 1] = "break" + elif lines[end + 1].strip() == "break": + del lines[end + 1] + else: + lines.insert(end + 1, "break") + return "\n".join(lines) + + +def find_full_bounds(puml, svg, clickedelement) -> tuple[int, int]: + lines = puml.splitlines() + count = index_of_clicked_activity(svg, clickedelement) + start = find_activity_start(lines, count) + end = find_activity_end(lines, start) + if lines[end + 1].startswith("note"): + while lines[end] != "end note": + end += 1 + if lines[end + 1] in ["detach", "break"]: + end += 1 + return start, end + + +def add_arrow_label(puml, svg, where, clickedelement): + lines = puml.splitlines() + start, end = find_full_bounds(puml, svg, clickedelement) + + # Find all existing arrow labels in the format "-> Arrow label x;" where x is an integer. + arrow_label_numbers = [] + for line in lines: + match = re.search(r"-> Arrow label (\d+);", line) + if match: + arrow_label_numbers.append(int(match.group(1))) + + # Determine the next arrow label number + if arrow_label_numbers: + next_label_number = max(arrow_label_numbers) + 1 + else: + next_label_number = 1 + + # Insert the arrow label at the correct position + if where == "above": + lines.insert(start, f"-> Arrow label {next_label_number};") + else: + lines.insert(end + 1, f"-> Arrow label {next_label_number};") + + return "\n".join(lines) + + +def svgtochunklist(svg: str) -> list[SvgChunk]: + chunks = [] + d = Pq(svg) + + rects = d("rect") + for rect in rects: + if ( + float(rect.get("height")) > 6 + and rect.get("style") == "stroke:#181818;stroke-width:0.5;" + ): # check that the rect is an activity and not a fork bar + rect = Pq(rect) # rect svg is self-closing, so no is in this. + rect_svg = str(rect) + rect_svg = rect_svg[:-2] + ">" + rect_obj = RectElement.from_svg(rect_svg) + + text_elements = [] + next_elem = rect.next() + + # Unified handling for both 'text' and 'a' elements + while next_elem and next_elem[0].tag in {"text", "a"}: + if next_elem[0].tag == "text": + # Handle text elements as before + element = TextElement.from_svg(next_elem) + next_elem = next_elem.next() + elif next_elem[0].tag == "a": + # Handle 'a' elements similarly to text + link_href = next_elem[0].get("href") + link_text = next_elem[0].find("text").text + + # Create a temporary TextElement for the tag, excluding the link text itself from the label + element = TextElement(label=f"[[{link_href} {link_text}]]") + element.x = float(next_elem[0].find("text").get("x")) + element.y = float(next_elem[0].find("text").get("y")) + next_elem = next_elem.next() + + # Append to the text elements list + text_elements.append(element) + + chunks.append(SvgChunk(object=rect_obj, text_elements=text_elements)) + + return chunks + + +def svgchunktotext(svgchunklist: list[SvgChunk], clickedsvg: RectElement): + text = "" + + for chunk in svgchunklist: + if chunk.object == clickedsvg: + previous_y = None # Initialize previous Y value + + for index, text_element in enumerate(chunk.text_elements): + if index == 0: + text += text_element.label + else: + # Compare current element's Y with the previous one + if text_element.y == previous_y: + text += " " + text_element.label # Same Y, add with space + else: + text += ( + "\n" + text_element.label + ) # Different Y, add with newline + + previous_y = text_element.y # Update previous Y value + + return text diff --git a/src/plantuml_gui/add.py b/src/plantuml_gui/add.py new file mode 100644 index 0000000..36b83b1 --- /dev/null +++ b/src/plantuml_gui/add.py @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +from typing import Literal + + +def add( + puml: str, + index: int, + type: Literal[ + "activity", + "connector", + "if", + "fork", + "while", + "start", + "stop", + "end", + "note", + "repeat", + "switch", + ], +): + lines = puml.splitlines() + + if type == "activity": + lines.insert(index, ":Activity;") + + if type == "connector": + lines.insert(index, "(C)") + + if type == "if": + if_lines = [ + "if (Statement) then (yes)", + ":Activity;", + "else (no)", + ":Activity;", + "endif", + ] + + lines = lines[:index] + if_lines + lines[index:] + + if type == "fork": + fork_lines = [ + "fork", + ":action;", + "fork again", + ":action;", + "end fork", + ] + + lines = lines[:index] + fork_lines + lines[index:] + + if type == "while": + while_lines = [ + "while (Statement) is (yes)", + ":Activity;", + "endwhile (no)", + ":Activity;", + ] + + lines = lines[:index] + while_lines + lines[index:] + + if type in ["start", "stop", "end"]: # pragma: no cover + lines.insert(index, type) + + if type == "note": + lines.insert(index, "end note") + lines.insert(index, "note") + lines.insert(index, "note right") + + if type == "repeat": + repeat_lines = [ + "repeat", + ":Activity;", + "backward:Activity;", + "repeat while (while ?) is (yes) not (no)", + ":Activity;", + ] + lines = lines[:index] + repeat_lines + lines[index:] + + if type == "switch": + switch_lines = [ + "switch (test?)", + "case ( condition 1)", + ":Activity;", + "case ( condition 2)", + ":Activity;", + "endswitch", + ] + lines = lines[:index] + switch_lines + lines[index:] + + return "\n".join(lines) diff --git a/src/plantuml_gui/app.py b/src/plantuml_gui/app.py new file mode 100644 index 0000000..1b3ee7b --- /dev/null +++ b/src/plantuml_gui/app.py @@ -0,0 +1,929 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import io + +from flask import Blueprint, Flask, jsonify, render_template, request, send_file +from plantuml_gui.classes import Ellipse, PolyElement, RectElement + +from .__about__ import __version__ +from .activity import ( + add_arrow_label, + add_note_activity, + break_activity, + check_backward, + delete_activity, + detach_activity, + edit_activity, + find_full_bounds, + find_text_bounds, + index_of_clicked_activity, + svgchunktotext, + svgtochunklist, +) +from .add import add +from .arrow import ( + check_for_duplicate_arrow, + delete_arrow, + edit_arrow, + get_arrow_line, + get_arrow_type, + svgtoarrowtext, +) +from .connector import ( + delete_connector, + detach_connector, + edit_connector_char, + find_index_connector, + get_connector_char, + get_index_connector, + svgtochunklistconnector, +) +from .ellipse import delete_ellipse_element, get_index_ellipse, svgtochunklistellipse +from .fork import ( + delete_fork2, + deletefork, + fork_again, + fork_toggle, + fork_toggle2, + svgtochunklistfork, +) +from .group import delete_group, edit_group, get_group_line, get_group_text +from .if_statements import ( + add_backwards, + check_if_repeat_has_backward, + check_what_poly, + deleteif, + detach_if, + edittextinternalif2, + get_if_line, + get_line_for_adding_into_if, + polychunktotext, + svgtochunklistpolygon, + switch_again, +) +from .merge import get_index_merge +from .note import delete_note, edit_note, get_note_line, get_note_text, note_toggle +from .puml_encoder import plantuml_decode, plantuml_encode +from .render import _create_png_from_uml, _create_svg_from_uml +from .title import ( + add_title, + delete_title, + edit_title_text, + find_title_bounds, + get_title_text, +) +from .whilepoly import ( + delete_while, + editwhile, + find_index_break, + find_index_loop, + get_while_line, + whiletotext, +) + +plantuml = Blueprint( + "plantuml_gui", + __name__, + template_folder="templates", + static_folder="static", +) + + +@plantuml.route("/") +def home(): + return render_template("index.html", version=__version__) # pragma: no cover + + +@plantuml.route("/render", methods=["POST"]) +def render(): + data = request.get_json() + puml = data["plantuml"] + return _create_svg_from_uml(puml) + + +@plantuml.route("/renderPNG", methods=["POST"]) +def renderpng(): + data = request.get_json() + puml = data["plantuml"] + + # Create the PNG image from the PlantUML code + image_bytes = _create_png_from_uml( + puml + ) # This function should return a byte stream of the image + + # Use io.BytesIO to handle the byte stream + image_stream = io.BytesIO(image_bytes) + + # Set the correct content type and disposition + return send_file( + image_stream, + mimetype="image/png", + as_attachment=True, + download_name="generated-image.png", # Filename when downloaded + ) + + +@plantuml.route("/editText", methods=["POST"]) +def edittext(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + newname = data["newname"] + clickedsvg = data["svgelement"] + clickedelement = RectElement.from_svg(clickedsvg) + return edit_activity(puml, svg, clickedelement, newname) + + +@plantuml.route("/getText", methods=["POST"]) +def gettext(): + data = request.get_json() + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = RectElement.from_svg(clickedelement) + svgchunklist = svgtochunklist(svg) + + return svgchunktotext(svgchunklist, clickedelement) + + +@plantuml.route("/deleteActivity", methods=["POST"]) +def deleteactivity(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = RectElement.from_svg(clickedelement) + + return delete_activity(puml, svg, clickedelement) + + +@plantuml.route("/addNoteActivity", methods=["POST"]) +def addnoteactivity(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = RectElement.from_svg(clickedelement) + + return add_note_activity(puml, svg, clickedelement) + + +@plantuml.route("/addToActivity", methods=["POST"]) +def addtoactivity(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + type = data["type"] + clickedelement = data["svgelement"] + clickedelement = RectElement.from_svg(clickedelement) + start, end = find_full_bounds(puml, svg, clickedelement) + return add(puml, end + 1, type) + + +@plantuml.route("/detachActivity", methods=["POST"]) +def detachactivity(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = RectElement.from_svg(clickedelement) + return detach_activity(puml, svg, clickedelement) + + +@plantuml.route("/breakActivity", methods=["POST"]) +def breakactivity(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = RectElement.from_svg(clickedelement) + return break_activity(puml, svg, clickedelement) + + +@plantuml.route("/checkBackward", methods=["POST"]) +def checkbackward(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = RectElement.from_svg(clickedelement) + return check_backward(puml, svg, clickedelement) + + +@plantuml.route("/getActivityLine", methods=["POST"]) +def getactivityline(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = RectElement.from_svg(clickedelement) + count = index_of_clicked_activity(svg, clickedelement) + lines = puml.splitlines() + result = find_text_bounds(lines, count) + return jsonify({"result": result}) # int is not accepted by flask + + +@plantuml.route("/addArrowLabel", methods=["POST"]) +def addarrowlabel(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + where = data["where"] + clickedelement = data["svgelement"] + clickedelement = RectElement.from_svg(clickedelement) + return add_arrow_label(puml, svg, where, clickedelement) + + +################# IF STATEMENTS ############### +################# IF STATEMENTS ############### +################# IF STATEMENTS ############### +################# IF STATEMENTS ############### +################# IF STATEMENTS ############### +################# IF STATEMENTS ############### +################# IF STATEMENTS ############### +################# IF STATEMENTS ############### +################# IF STATEMENTS ############### +################# IF STATEMENTS ############### + + +# @plantuml.route("/addNoteIf", methods=["POST"]) +# def addnoteif(): +# data = request.get_json() +# puml = data["plantuml"] +# svg = data["svg"] +# clickedsvg = data["svgelement"] +# clickedelement = PolyElement.from_svg(clickedsvg) +# svgchunklist = svgtochunklistpolygon(svg) +# return add_note_if(puml, svgchunklist, clickedelement) + + +@plantuml.route("/checkWhatPoly", methods=["POST"]) +def checkwhatpoly(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + + svgchunklist = svgtochunklistpolygon(svg) + clickedelement = PolyElement.from_svg(clickedsvg) + return check_what_poly(puml, svgchunklist, clickedelement) + + +@plantuml.route("/checkIfRepeatHasBackward", methods=["POST"]) +def checkifrepeathasbackward(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + + svgchunklist = svgtochunklistpolygon(svg) + clickedelement = PolyElement.from_svg(clickedsvg) + return check_if_repeat_has_backward(puml, svgchunklist, clickedelement) + + +@plantuml.route("/addBackwards", methods=["POST"]) +def addbackwards(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + + svgchunklist = svgtochunklistpolygon(svg) + clickedelement = PolyElement.from_svg(clickedsvg) + return add_backwards(puml, svgchunklist, clickedelement) + + +@plantuml.route("/editTextIf", methods=["POST"]) +def edittextif(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + statement = data["statement"] + branch1 = data["branch1"] + branch2 = data["branch2"] + clickedsvg = data["svgelement"] + + svgchunklist = svgtochunklistpolygon(svg) + clickedelement = PolyElement.from_svg(clickedsvg) + return edittextinternalif2( + puml, svgchunklist, statement, branch1, branch2, clickedelement + ) + + +@plantuml.route("/getTextPoly", methods=["POST"]) +def gettextpoly(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = PolyElement.from_svg(clickedelement) + svgchunklist = svgtochunklistpolygon(svg) + return polychunktotext(puml, svgchunklist, clickedelement) + + +@plantuml.route("/delIf", methods=["POST"]) +def delif(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = PolyElement.from_svg(clickedsvg) + svgchunklist = svgtochunklistpolygon(svg) + return deleteif(puml, svgchunklist, clickedelement) + + +@plantuml.route("/switchAgain", methods=["POST"]) +def switchagain(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = PolyElement.from_svg(clickedsvg) + svgchunklist = svgtochunklistpolygon(svg) + return switch_again(puml, svgchunklist, clickedelement) + + +@plantuml.route("/getIfLine", methods=["POST"]) +def getifline(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = PolyElement.from_svg(clickedelement) + svgchunklist = svgtochunklistpolygon(svg) + result = get_if_line(puml, svgchunklist, clickedelement) + return jsonify({"result": result}) # int is not accepted by flask + + +@plantuml.route("/addToIf", methods=["POST"]) +def addtoif(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + where = data["where"] + type = data["type"] + clickedelement = PolyElement.from_svg(clickedsvg) + index = get_line_for_adding_into_if(puml, svg, clickedelement, where) + return add(puml, index, type) + + +@plantuml.route("/detachIf", methods=["POST"]) +def detachif(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = PolyElement.from_svg(clickedsvg) + svgchunklist = svgtochunklistpolygon(svg) + return detach_if(puml, svgchunklist, clickedelement) + + +################ Ellipses ############################ +################ Ellipses ############################ +################ Ellipses ############################ +################ Ellipses ############################ +################ Ellipses ############################ +################ Ellipses ############################ +################ Ellipses ############################ + + +@plantuml.route("/addToEllipse", methods=["POST"]) +def addtoellipse(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + where = data["where"] + type = data["type"] + clickedsvg = data["svgelement"] + clickedelement = Ellipse.from_svg(clickedsvg) + svgchunklist = svgtochunklistellipse(svg) + index = get_index_ellipse(puml, svgchunklist, clickedelement, where) + return add(puml, index, type) + + +@plantuml.route("/deleteEllipse", methods=["POST"]) +def deleteellipse(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = Ellipse.from_svg(clickedsvg) + svgchunklist = svgtochunklistellipse(svg) + return delete_ellipse_element(puml, svgchunklist, clickedelement) + + +@plantuml.route("/getEllipseLine", methods=["POST"]) +def getellipseline(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = Ellipse.from_svg(clickedelement) + svgchunklist = svgtochunklistellipse(svg) + result = get_index_ellipse(puml, svgchunklist, clickedelement, "where") + return jsonify({"result": result}) # int is not accepted by flask + + +################################## TITLE ############################# +################################## TITLE ############################# +################################## TITLE ############################# +################################## TITLE ############################# +################################## TITLE ############################# + + +@plantuml.route("/addTitle", methods=["POST"]) +def addtitle(): + data = request.get_json() + puml = data["plantuml"] + return add_title(puml) + + +@plantuml.route("/getTextTitle", methods=["POST"]) +def gettexttile(): + data = request.get_json() + puml = data["plantuml"] + return get_title_text(puml) + + +@plantuml.route("/editTitle", methods=["POST"]) +def edittitle(): + data = request.get_json() + puml = data["plantuml"] + title = data["title"] + return edit_title_text(puml, title) + + +@plantuml.route("/getTitleLine", methods=["POST"]) +def gettitle(): + data = request.get_json() + puml = data["plantuml"] + lines = puml.splitlines() + result = find_title_bounds(lines) + return jsonify({"result": result}) + + +@plantuml.route("/deleteTitle", methods=["POST"]) +def deletetitle(): + data = request.get_json() + puml = data["plantuml"] + return delete_title(puml) + + +#################################### FORK ################################# +#################################### FORK ################################# +#################################### FORK ################################# +#################################### FORK ################################# +#################################### FORK ################################# +#################################### FORK ################################# + + +# @plantuml.route("/addNoteFork", methods=["POST"]) +# def addnotefork(): +# data = request.get_json() +# puml = data["plantuml"] +# svg = data["svg"] +# clickedsvg = data["svgelement"] +# clickedelement = RectElement.from_svg(clickedsvg) +# svgchunklist = svgtochunklistfork(svg) +# return add_note_fork(puml, svgchunklist, clickedelement) + + +@plantuml.route("/deleteFork", methods=["POST"]) +def delfork(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = RectElement.from_svg(clickedsvg) + svgchunklist = svgtochunklistfork(svg) + return deletefork(puml, svgchunklist, clickedelement) + + +@plantuml.route("/forkAgain", methods=["POST"]) +def forkagain(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = RectElement.from_svg(clickedsvg) + svgchunklist = svgtochunklistfork(svg) + return fork_again(puml, svgchunklist, clickedelement) + + +@plantuml.route("/forkToggle", methods=["POST"]) +def forktoggle(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = RectElement.from_svg(clickedsvg) + svgchunklist = svgtochunklistfork(svg) + return fork_toggle(puml, svgchunklist, clickedelement) + + +@plantuml.route("/forkToggle2", methods=["POST"]) +def forktoggle2(): + data = request.get_json() + puml = data["plantuml"] + index = data["line"] + return fork_toggle2(puml, index) + + +@plantuml.route("/deleteFork2", methods=["POST"]) +def deletefork2(): + data = request.get_json() + puml = data["plantuml"] + index = data["line"] + return delete_fork2(puml, index) + + +@plantuml.route("/addToFork", methods=["POST"]) +def addtofork(): + data = request.get_json() + puml = data["plantuml"] + index = data["line"] + type = data["type"] + return add(puml, index + 1, type) + + +############################# Note ################################# +############################# Note ################################# +############################# Note ################################# +############################# Note ################################# +############################# Note ################################# +############################# Note ################################# + + +@plantuml.route("/getNoteText", methods=["POST"]) +def getnotetext(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + return get_note_text(puml, svg, clickedelement) + + +@plantuml.route("/editNote", methods=["POST"]) +def editnote(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + text = data["text"] + clickedelement = data["svgelement"] + return edit_note(puml, svg, clickedelement, text) + + +@plantuml.route("/deleteNote", methods=["POST"]) +def deletenote(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + return delete_note(puml, svg, clickedelement) + + +@plantuml.route("/noteToggle", methods=["POST"]) +def notetoggle(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + return note_toggle(puml, svg, clickedelement) + + +@plantuml.route("/getNoteLine", methods=["POST"]) +def getnoteline(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + result = get_note_line(puml, svg, clickedelement) + return jsonify({"result": result}) # int is not accepted by flask + + +########################## GROUP ############################# +########################## GROUP ############################# +########################## GROUP ############################# +########################## GROUP ############################# +########################## GROUP ############################# +########################## GROUP ############################# + + +@plantuml.route("/getGroupText", methods=["POST"]) +def getgrouptext(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + return get_group_text(puml, svg, clickedelement) + + +@plantuml.route("/getGroupLine", methods=["POST"]) +def getgroupline(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + result = get_group_line(puml, svg, clickedelement) + return jsonify({"result": result}) # int is not accepted by flask + + +@plantuml.route("/editGroup", methods=["POST"]) +def editgroup(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + text = data["text"] + return edit_group(puml, svg, clickedelement, text) + + +@plantuml.route("/deleteGroup", methods=["POST"]) +def deletegroup(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + return delete_group(puml, svg, clickedelement) + + +####################### MERGE ########################## +####################### MERGE ########################## +####################### MERGE ########################## +####################### MERGE ########################## +####################### MERGE ########################## + + +@plantuml.route("/getMergeLine", methods=["POST"]) +def getmergeline(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + result = get_index_merge(puml, svg, clickedelement) + return jsonify({"result": result}) # int is not accepted by flask + + +@plantuml.route("/addToMerge", methods=["POST"]) +def addtomerge(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + type = data["type"] + index = get_index_merge(puml, svg, clickedelement) + print(index) + return add(puml, index + 1, type) + + +######################################## WHILE ######################################## +######################################## WHILE ######################################## +######################################## WHILE ######################################## +######################################## WHILE ######################################## +######################################## WHILE ######################################## + + +@plantuml.route("/getTextWhile", methods=["POST"]) +def gettextwhile(): + data = request.get_json() + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = PolyElement.from_svg(clickedelement) + svgchunklist = svgtochunklistpolygon(svg) + return whiletotext(svgchunklist, clickedelement) + + +@plantuml.route("/editTextWhile", methods=["POST"]) +def edittextwhile(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = PolyElement.from_svg(clickedelement) + svgchunklist = svgtochunklistpolygon(svg) + whilestatement = data["whilestatement"] + breakstatement = data["break"] + loop = data["loop"] + return editwhile( + puml, svgchunklist, whilestatement, breakstatement, loop, clickedelement + ) + + +@plantuml.route("/delWhile", methods=["POST"]) +def delwhile(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = PolyElement.from_svg(clickedelement) + svgchunklist = svgtochunklistpolygon(svg) + return delete_while(puml, svgchunklist, clickedelement) + + +@plantuml.route("/addToWhile", methods=["POST"]) +def addactivitywhile(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = PolyElement.from_svg(clickedelement) + type = data["type"] + where = data["where"] + svgchunklist = svgtochunklistpolygon(svg) + if where == "loop": + index = find_index_loop(puml, svgchunklist, clickedelement) + else: + index = find_index_break(puml, svgchunklist, clickedelement) + return add(puml, index, type) + + +@plantuml.route("/getWhileLine", methods=["POST"]) +def getwhileline(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + clickedelement = PolyElement.from_svg(clickedelement) + svgchunklist = svgtochunklistpolygon(svg) + result = get_while_line(puml, svgchunklist, clickedelement) + return jsonify({"result": result}) # int is not accepted by flask + + +########################### Connector ######################### +########################### Connector ######################### +########################### Connector ######################### +########################### Connector ######################### +########################### Connector ######################### + + +@plantuml.route("/editCharConnector", methods=["POST"]) +def editcharconnector(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + text = data["text"] + clickedsvg = data["svgelement"] + clickedelement = Ellipse.from_svg(clickedsvg) + svgchunklist = svgtochunklistconnector(svg) + return edit_connector_char(puml, svgchunklist, clickedelement, text) + + +@plantuml.route("/getCharConnector", methods=["POST"]) +def getcharconnector(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = Ellipse.from_svg(clickedsvg) + svgchunklist = svgtochunklistconnector(svg) + return get_connector_char(puml, svgchunklist, clickedelement) + + +@plantuml.route("/connectorDelete", methods=["POST"]) +def connectordelete(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = Ellipse.from_svg(clickedsvg) + svgchunklist = svgtochunklistconnector(svg) + return delete_connector(puml, svgchunklist, clickedelement) + + +@plantuml.route("/getConnectorLine", methods=["POST"]) +def getconnectorline(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = Ellipse.from_svg(clickedsvg) + svgchunklist = svgtochunklistconnector(svg) + result = find_index_connector(puml, svgchunklist, clickedelement) + return jsonify({"result": result}) # int is not accepted by flask + + +@plantuml.route("/detachConnector", methods=["POST"]) +def detachconnector(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + clickedelement = Ellipse.from_svg(clickedsvg) + svgchunklist = svgtochunklistconnector(svg) + return detach_connector(puml, svgchunklist, clickedelement) + + +@plantuml.route("/addToConnector", methods=["POST"]) +def addtoconnector(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedsvg = data["svgelement"] + where = data["where"] + type = data["type"] + clickedelement = Ellipse.from_svg(clickedsvg) + svgchunklist = svgtochunklistconnector(svg) + start, end = get_index_connector(puml, svgchunklist, clickedelement, where) + return add(puml, end + 1, type) + + +################################# ARROW ##################################### +################################# ARROW ##################################### +################################# ARROW ##################################### +################################# ARROW ##################################### +################################# ARROW ##################################### + + +@plantuml.route("/delArrow", methods=["POST"]) +def delarrow(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + return delete_arrow(puml, svg, clickedelement) + + +@plantuml.route("/checkDuplicateArrow", methods=["POST"]) +def checkdupearrow(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + result = check_for_duplicate_arrow(puml, svg, clickedelement) + arrow_type = get_arrow_type(puml, svg, clickedelement) + return jsonify({"result": result, "type": arrow_type}) + + +@plantuml.route("/getArrowText", methods=["POST"]) +def getarrowtext(): + data = request.get_json() + svg = data["svg"] + clickedelement = data["svgelement"] + return svgtoarrowtext(svg, clickedelement) + + +@plantuml.route("/editArrow", methods=["POST"]) +def editarrow(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + text = data["text"] + clickedelement = data["svgelement"] + return edit_arrow(puml, svg, text, clickedelement) + + +@plantuml.route("/getArrowLine", methods=["POST"]) +def getarrowline(): + data = request.get_json() + puml = data["plantuml"] + svg = data["svg"] + clickedelement = data["svgelement"] + result = get_arrow_line(puml, svg, clickedelement) + return jsonify({"result": result}) # int is not accepted by flask + + +################################# ENCODE DECODE ################################# +################################# ENCODE DECODE ################################# +################################# ENCODE DECODE ################################# +################################# ENCODE DECODE ################################# +################################# ENCODE DECODE ################################# + + +@plantuml.route("/encode", methods=["POST"]) +def encode(): + data = request.get_json() + puml = data["plantuml"] + return plantuml_encode(puml) + + +@plantuml.route("/decode", methods=["POST"]) +def decode(): + data = request.get_json() + hash = data["hash"] + return plantuml_decode(hash) + + +app = Flask(__name__) +app.register_blueprint(plantuml) diff --git a/src/plantuml_gui/arrow.py b/src/plantuml_gui/arrow.py new file mode 100644 index 0000000..9c2fafb --- /dev/null +++ b/src/plantuml_gui/arrow.py @@ -0,0 +1,177 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import re + +from pyquery import PyQuery as Pq # pragma: no cover + + +def svgtoarrowtext(svg, clickedelement): # works for arrows and switch condition text + text = [] + d = Pq(svg) + + polys = d("polygon") + for poly in polys: + poly = Pq(poly) + poly_svg = str(poly) + poly_svg = poly_svg[:-2] + ">" + if poly_svg == clickedelement: + next_elem = poly.next() + while next_elem and next_elem[0].tag == "text": + text.append(next_elem.text()) + next_elem = next_elem.next() + return "\n".join(text) + + +def check_for_duplicate_arrow(puml, svg, clickedelement): + numbers = [] + arrow_text = svgtoarrowtext(svg, clickedelement) + lines = puml.splitlines() + start, end = -1, -1 + for index, line in enumerate(lines): + start, end = -1, -1 + clean_line = line.strip() + + if clean_line.startswith("case"): # condition text + start, end = index, index + condition_text = re.search(r"\(([^)]+)\)", lines[index]).group(1).strip() + if condition_text.replace("\\n", "\n") == arrow_text.replace("\\n", "\n"): + numbers.append(start) + + elif clean_line.startswith("-"): + start = index + while not lines[index].strip().endswith(";"): + index += 1 + end = index + + arrow_lines = lines[start : end + 1] + stripped_arrow_lines = [] + for line in arrow_lines: + stripped_arrow_lines.append(line.strip()) + puml_arrow_text = "\n".join(stripped_arrow_lines) + cleaned_text = puml_arrow_text.strip() + pattern = r"^.*?>\s*" + cleaned_text = re.sub(pattern, "", cleaned_text) + cleaned_text = cleaned_text[:-1] + if cleaned_text.replace("\\n", "\n") == arrow_text.replace("\\n", "\n"): + numbers.append(start) + return len(numbers) > 1 + + +def find_arrow_bounds(puml, svg, clickedelement) -> tuple[int, int]: + arrow_text = svgtoarrowtext(svg, clickedelement) + lines = puml.splitlines() + start, end = -1, -1 + for index, line in enumerate(lines): + start, end = -1, -1 + clean_line = line.strip() + + if clean_line.startswith("case"): # condition text + start, end = index, index + match = re.search(r"\(([^)]+)\)", lines[index]) + if match: + condition_text = match.group(1).strip() + if condition_text.replace("\\n", "\n") == arrow_text.replace("\\n", "\n"): + break + + elif clean_line.startswith("-"): + start = index + while not lines[index].strip().endswith(";"): + index += 1 + end = index + + arrow_lines = lines[start : end + 1] + stripped_arrow_lines = [] + for line in arrow_lines: + stripped_arrow_lines.append(line.strip()) + puml_arrow_text = "\n".join(stripped_arrow_lines) + cleaned_text = puml_arrow_text.strip() + pattern = r"^.*?>\s*" + cleaned_text = re.sub(pattern, "", cleaned_text) + cleaned_text = cleaned_text[:-1] + if cleaned_text.replace("\\n", "\n") == arrow_text.replace("\\n", "\n"): + break + return start, end + + +def get_arrow_line(puml, svg, clickedelement): + start, end = find_arrow_bounds(puml, svg, clickedelement) + return start, end + + +def get_arrow_type(puml, svg, clickedelement): + lines = puml.splitlines() + start, end = find_arrow_bounds(puml, svg, clickedelement) + if lines[start].strip().startswith("case"): + return "case" + else: + return "arrow" + + +def edit_arrow(puml, svg, text, clickedelement): + lines = puml.splitlines() + start, end = find_arrow_bounds(puml, svg, clickedelement) + arrow_lines = lines[start : end + 1] + + if lines[start].startswith("case"): + arrow_text = "\n".join(arrow_lines) + arrow_text = re.sub(r"\(([^)]+)\)", f"({text})", arrow_text, flags=re.DOTALL) + lines[start] = arrow_text.replace( + "\n", "\\n" + ) # case condition is always on the same line in the editor + + else: + arrow_text = "\n".join(arrow_lines) + arrow_text = re.sub(r">(.*?)\;", f">{text};", arrow_text, flags=re.DOTALL) + + arrow_lines = arrow_text.splitlines() + lines[start : end + 1] = arrow_lines + + return "\n".join(lines) + + +def delete_arrow(puml, svg, clickedelement): + start, end = find_arrow_bounds(puml, svg, clickedelement) + lines = puml.splitlines() + + if lines[start].startswith("case"): + index = start + level = 0 + while index < len(lines) - 1: + if lines[index + 1].strip().startswith("switch"): + level += 1 + if level == 0: + if ( + lines[index + 1].strip().startswith("case") + or lines[index + 1].strip() == "endswitch" + ): + end = index + break + else: + if lines[index + 1].strip() == "endswitch": + level -= 1 + index += 1 + + del lines[start : end + 1] + return "\n".join(lines) diff --git a/src/plantuml_gui/classes.py b/src/plantuml_gui/classes.py new file mode 100644 index 0000000..442668c --- /dev/null +++ b/src/plantuml_gui/classes.py @@ -0,0 +1,322 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from dataclasses import dataclass + +from pyquery import PyQuery as Pq # pragma: no cover + + +@dataclass +class Activity: + label: str + + +@dataclass +class If: + statement: str + branch1: str + branch2: str + + +@dataclass +class PumlChunk: + text: str + object: Activity | If | None + + +@dataclass +class Ellipse: + cx: float + cy: float + + def __eq__(self, other): + return ( + isinstance(other, Ellipse) and self.cx == other.cx and self.cy == other.cy + ) + + @classmethod + def from_svg(cls, svgtext: str): + svg = Pq(svgtext) + ellipse = svg("ellipse") + return cls(float(ellipse.attr("cx")), float(ellipse.attr("cy"))) # type: ignore + + +@dataclass +class PolyElement: + points: str + + def __eq__(self, other): + return isinstance(other, PolyElement) and self.points == other.points + + @classmethod + def from_svg(cls, svgtext: str): + svg = Pq(svgtext) + poly = svg("polygon") + return cls(str(poly.attr("points"))) + + def get_points(self): + elements = self.points.split(",") + pairs = [(elements[i], elements[i + 1]) for i in range(0, len(elements) - 1, 2)] + + unique_pairs = [] + seen = set() + + for pair in pairs: + identifier = tuple(pair) + if identifier not in seen: + seen.add(identifier) + unique_pairs.append(pair) + + return unique_pairs + + def is_merge( + self, + ): # We need to check if its not a merge polygon. Merge polygons after switch statements have equal points to statement polys. + elements = self.points.split(",") + pairs = [(elements[i], elements[i + 1]) for i in range(0, len(elements) - 1, 2)] + + unique_pairs = [] + seen = set() + + for pair in pairs: + identifier = tuple(pair) + if identifier not in seen: + seen.add(identifier) + unique_pairs.append(pair) + + return len(unique_pairs) != 6 + + +@dataclass(kw_only=True) +class TreeNode: + index: int + + def add_node(self, node: "TreeNode"): + pass # pragma: no cover + + def add_indices(self, indices: list[int], lines: list[str]): + pass # pragma: no cover + + +@dataclass(kw_only=True) +class IfElseNode(TreeNode): + ifbranch: list["TreeNode"] + elsebranch: list["TreeNode"] + inside_ifbranch: bool = True + + def add_node(self, node: TreeNode): + if self.inside_ifbranch: + self.ifbranch.append(node) + else: + self.elsebranch.append(node) + + def add_indices(self, indices: list[int], lines: list[str]): + if not check_branch(lines, self.index): + indices.append(self.index) + for node in self.ifbranch: + node.add_indices(indices, lines) + for node in self.elsebranch: + node.add_indices(indices, lines) + if check_branch(lines, self.index): + indices.append(self.index) + + +@dataclass(kw_only=True) +class RepeatSwitchNode(TreeNode): + branch: list["TreeNode"] + + def add_node(self, node: TreeNode): + self.branch.append(node) + + def add_indices(self, indices: list[int], lines: list[str]): + if not lines[self.index].strip() == "repeat": + indices.append(self.index) + for node in self.branch: + node.add_indices(indices, lines) + if lines[self.index].strip() == "repeat": + indices.append(self.index) + + +@dataclass +class RectElement: + x: float + y: float + + def __eq__(self, other): + return ( + isinstance(other, RectElement) and self.x == other.x and self.y == other.y + ) + + @classmethod + def from_svg(cls, svgtext: str): + svg = Pq(svgtext) + rect = svg("rect") + return cls(float(rect.attr("x")), float(rect.attr("y"))) # type: ignore + + +@dataclass +class TextElement: + label: str + x: float | None = None + y: float | None = None + + @classmethod + def from_svg(cls, svgtext: str | Pq): + svg = Pq(svgtext) + text = svg("text") + return cls(text.text(), float(text.attr("x")), float(text.attr("y"))) # type: ignore + + +@dataclass +class SvgChunk: + object: RectElement | PolyElement | Ellipse + text_elements: list[TextElement] + + +def check_branch( + lines, index +): # checks if we need to increment nesting due to if branch + else_start, else_end = findelsebounds(lines, index) + end = find_end(lines, index) + if ( + else_end == -1 # no else branch + or end == else_end + 1 # empty else branch + or index == else_start - 1 # empty if branch + ): + return True + else: + if else_start - index == 2: + if lines[index + 1].strip().startswith("(") or lines[ + index + 1 + ].strip().startswith("stop"): + return True + if end - else_end == 2: + if lines[else_end + 1].strip().startswith("(") or lines[ + else_end + 1 + ].strip().startswith("stop"): + return True + + return False + + +def findelsebounds(lines, if_start): + start_else = -1 + end_else = -1 + index = if_start + inside_else = False + parentheses = 0 + + level = 0 + while index < len(lines): # find index of correct else line + line = lines[index] + clean_line = line.strip() + if ( + level == 1 + ): # if at level 1 its the correct else (level will always start off as 1 since the first line is the if statement were on) + if clean_line.startswith("else"): + start_else = index + inside_else = True + if clean_line.startswith( + "if" + ): # nested if statement, we need to find two elses. + level += 1 + if level != 1 and clean_line.startswith("else"): + level -= 1 + + if inside_else: + parentheses += clean_line.count("(") + parentheses -= clean_line.count(")") + if parentheses == 0: + end_else = index + break + index += 1 + + return start_else, end_else + + +def find_end(lines, start_if): + end_if = -1 + index = start_if + level = 0 + start_line = lines[start_if].strip() + + if start_line == "repeat": + while index < len(lines): + line = lines[index] + clean_line = line.strip() + + if level == 1: + if clean_line.startswith("repeat while") or clean_line.startswith( + "repeatwhile" + ): + end_if = index + break + + if clean_line == "repeat": + level += 1 + + if ( + level != 1 + and clean_line.startswith("repeat while") + or clean_line.startswith("repeatwhile") + ): + level -= 1 + + index += 1 + elif start_line.startswith("if"): + while index < len(lines): + line = lines[index] + clean_line = line.strip() + + if level == 1: + if clean_line.startswith("endif"): + end_if = index + break + + if clean_line.startswith("if"): + level += 1 + + if level != 1 and clean_line.startswith("endif"): + level -= 1 + + index += 1 + elif start_line.startswith("switch"): + while index < len(lines): + line = lines[index] + clean_line = line.strip() + + if level == 1: + if clean_line.startswith("endswitch"): + end_if = index + break + + if clean_line.startswith("switch"): + level += 1 + + if level != 1 and clean_line.startswith("endswitch"): + level -= 1 + + index += 1 + + return end_if diff --git a/src/plantuml_gui/connector.py b/src/plantuml_gui/connector.py new file mode 100644 index 0000000..0768a3c --- /dev/null +++ b/src/plantuml_gui/connector.py @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import re + +from plantuml_gui.util import index_of_clicked_element # pragma: no cover +from pyquery import PyQuery as Pq + +from .classes import Ellipse, SvgChunk + + +def svgtochunklistconnector(svg): + chunks = [] + d = Pq(svg) + + ellipses = d("ellipse") + for ellipse in ellipses: + ellipse = Pq( + ellipse + ) # ellipse svg is self closing, so no is in this. + ellipse_svg = str(ellipse) + ellipse_svg = ellipse_svg[:-2] + ">" + ellipse_obj = Ellipse.from_svg(ellipse_svg) + + next_elem = ellipse.next() + if ( + next_elem + and next_elem[0].tag == "path" + and next_elem[0].get("fill") == "#000000" + ): + chunks.append(SvgChunk(object=ellipse_obj, text_elements=[])) + return chunks + + +def delete_connector(puml, svgchunklist, clickedelement): + start, end = get_index_connector(puml, svgchunklist, clickedelement, "below") + lines = puml.splitlines() + del lines[start : end + 1] + return "\n".join(lines) + + +def get_index_connector(puml, svgchunklist, clickedelement, where) -> tuple[int, int]: + index = 0 + count = index_of_clicked_element(svgchunklist, clickedelement) + lines = puml.splitlines() + for index, line in enumerate(lines): + clean_line = line.strip() + if clean_line.startswith("(") or ( + clean_line.startswith("#") and clean_line.endswith(")") + ): + count -= 1 + if count == 0: + if clean_line.startswith("(") or ( + clean_line.startswith("#") and clean_line.endswith(")") + ): + start = index + if where == "below": + if lines[index + 1].startswith("note"): + while lines[index] != "end note": + index += 1 + if lines[index + 1] == "detach": + index += 1 + break + end = index + return start, end + + +def find_index_connector(puml, svgchunklist, clickedelement): + index = 0 + count = index_of_clicked_element(svgchunklist, clickedelement) + lines = puml.splitlines() + while index < len(lines): + line = lines[index] + clean_line = line.strip() + if clean_line.startswith("(") or ( + clean_line.startswith("#") and clean_line.endswith(")") + ): + count -= 1 + if count == 0: + if clean_line.startswith("(") or ( + clean_line.startswith("#") and clean_line.endswith(")") + ): + return index + 1 + index += 1 + + +def detach_connector(puml, svgchunklist, clickedelement): + start, end = get_index_connector(puml, svgchunklist, clickedelement, "below") + lines = puml.splitlines() + if lines[end].strip().startswith("detach"): + del lines[end] + else: + lines.insert(end + 1, "detach") + return "\n".join(lines) + + +def get_connector_char(puml, svgchunklist, clickedelement) -> str: + lines = puml.splitlines() + index = find_index_connector(puml, svgchunklist, clickedelement) + matching_text = "" + if match := re.search(r"\((.)\)", lines[index - 1]): + matching_text = match.group(1) + return matching_text + + +def edit_connector_char(puml, svgchunklist, clickedelement, text): + lines = puml.splitlines() + index = find_index_connector(puml, svgchunklist, clickedelement) + lines[index - 1] = re.sub(r"\((.*?)\)", f"({text})", lines[index - 1]) + return "\n".join(lines) diff --git a/src/plantuml_gui/ellipse.py b/src/plantuml_gui/ellipse.py new file mode 100644 index 0000000..9254138 --- /dev/null +++ b/src/plantuml_gui/ellipse.py @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +from plantuml_gui.util import index_of_clicked_element # pragma: no cover +from pyquery import PyQuery as Pq + +from .classes import Ellipse, SvgChunk + + +def svgtochunklistellipse(svg): + chunks = [] + d = Pq(svg) + + ellipses = d("ellipse") + for ellipse in ellipses: + ellipse = Pq( + ellipse + ) # ellipse svg is self closing, so no is in this. + ellipse_svg = str(ellipse) + ellipse_svg = ellipse_svg[:-2] + ">" + ellipse_obj = Ellipse.from_svg(ellipse_svg) + + next_elem = ellipse.next() + if ( + next_elem and next_elem[0].tag == "ellipse" + ): # this part is done because the "end" element has two ellipses and we only want to count one of + next_elem = Pq(next_elem) + next_elem_svg = str(next_elem) + next_elem_svg = next_elem_svg[:-2] + ">" + next_elem_obj = Ellipse.from_svg(next_elem_svg) + if next_elem_obj == ellipse_obj: + continue + if ( + next_elem + and next_elem[0].tag == "path" + and next_elem[0].get("fill") == "#000000" + ): + continue + chunks.append(SvgChunk(object=ellipse_obj, text_elements=[])) + return chunks + + +def delete_ellipse_element(puml, svgchunklist, clickedelement): + index = 0 + count = index_of_clicked_element(svgchunklist, clickedelement) + lines = puml.splitlines() + while index < len(lines): + line = lines[index] + clean_line = line.strip() + if not lines[index - 1].startswith("note"): + if clean_line.startswith(("stop", "start", "end")): + count -= 1 + if count == 0: + if clean_line.startswith(("stop", "start", "end")): + del lines[index] + break + index += 1 + return "\n".join(lines) + + +def get_index_ellipse(puml, svgchunklist, clickedelement, where) -> int: + index = 0 + count = index_of_clicked_element(svgchunklist, clickedelement) + lines = puml.splitlines() + for index, line in enumerate(lines): + clean_line = line.strip() + if not lines[index - 1].startswith("note"): + if clean_line in ["stop", "start", "end"]: + count -= 1 + if count == 0: + if clean_line in ["stop", "start", "end"]: + break + return index + 1 diff --git a/src/plantuml_gui/fork.py b/src/plantuml_gui/fork.py new file mode 100644 index 0000000..5acd6b8 --- /dev/null +++ b/src/plantuml_gui/fork.py @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from pyquery import PyQuery as Pq # pragma: no cover + +from .classes import RectElement, SvgChunk + + +def svgtochunklistfork(svg: str) -> list[SvgChunk]: + chunks = [] + d = Pq(svg) + + rects = d("rect") + for rect in rects: + if float(rect.get("height")) == 6: + rect = Pq(rect) + rect_svg = str(rect) + rect_svg = rect_svg[:-2] + ">" + rect_obj = RectElement.from_svg(rect_svg) + chunks.append(SvgChunk(object=rect_obj, text_elements=[])) + return chunks + + +def findforkbounds(lines, count): + start_fork = -1 + end_fork = -1 + index = 0 + level = 0 + inside_fork = False + + while index < len(lines): + line = lines[index] + clean_line = line.strip() + + if clean_line == "fork": + if count == 1 and not inside_fork: + start_fork = index + inside_fork = True + continue + count -= 1 + if inside_fork and clean_line == "fork": + level += 1 + if ( + clean_line == "end fork" or clean_line == "end merge" + ): # fork can end in both + if inside_fork: + level -= 1 + if level == 0: + end_fork = index + break + if ( + clean_line == "end fork" + ): # if its an end merge there wasnt an extra count due to the rect. + count -= 1 + index += 1 + return start_fork, end_fork + + +def forkcount(svgchunklist: list[SvgChunk], clickedelement: RectElement): + count = 0 + for svgchunk in svgchunklist: # counts occurances of poly chunks (if statements) + count += 1 + if svgchunk.object == clickedelement: + print("found clicked fork") + break + return count + + +# def add_note_fork(puml: str, svgchunklist: list[SvgChunk], clickedelement: RectElement): +# count = forkcount(svgchunklist, clickedelement) +# lines = puml.splitlines() + +# start_fork, end_fork = findforkbounds(lines, count) + +# lines.insert(end_fork + 1, "end note") +# lines.insert(end_fork + 1, "note") +# lines.insert(end_fork + 1, "note right") +# return "\n".join(lines) + + +def deletefork(puml: str, svgchunklist: list[SvgChunk], clickedelement: RectElement): + count = forkcount(svgchunklist, clickedelement) + lines = puml.splitlines() + + start_fork, end_fork = findforkbounds(lines, count) + del lines[start_fork : end_fork + 1] + return "\n".join(lines) + + +def fork_again(puml: str, svgchunklist: list[SvgChunk], clickedelement: RectElement): + count = forkcount(svgchunklist, clickedelement) + lines = puml.splitlines() + + start_fork, end_fork = findforkbounds(lines, count) + lines.insert(end_fork, " :action;") + lines.insert(end_fork, "fork again") + return "\n".join(lines) + + +def fork_toggle(puml: str, svgchunklist: list[SvgChunk], clickedelement: RectElement): + count = forkcount(svgchunklist, clickedelement) + lines = puml.splitlines() + + start_fork, end_fork = findforkbounds(lines, count) + if lines[end_fork].strip() == "end fork": + lines[end_fork] = "end merge" + else: + lines[end_fork] = "end fork" + return "\n".join(lines) + + +def fork_toggle2(puml, index): + lines = puml.splitlines() + if lines[index].strip() == "end fork": + lines[index] = "end merge" + return "\n".join(lines) + + +def delete_fork2(puml, index): + end = index + start = -1 + i = index - 1 + level = 0 + lines = puml.splitlines() + + while i > 0: + line = lines[i].strip() + if line == "fork": + if level == 0: + start = i + break + else: + level -= 1 + if line in ["end merge", "end fork"]: + level += 1 + i -= 1 + + del lines[start : end + 1] + return "\n".join(lines) diff --git a/src/plantuml_gui/group.py b/src/plantuml_gui/group.py new file mode 100644 index 0000000..5c11a65 --- /dev/null +++ b/src/plantuml_gui/group.py @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from typing import Literal + +from pyquery import PyQuery as Pq # pragma: no cover + + +def group_count(svg, clickedelement): + count = 0 + d = Pq(svg) + + paths = d("path") + for path in paths: + path = Pq(path) + path_svg = str(path) + path_svg = path_svg[:-2] + ">" + if path[0].get("style") == "stroke:#000000;stroke-width:1.5;": + count += 1 + if path_svg == clickedelement: + break + return count + + +def find_group_bounds(lines, count): + group_start, group_end = -1, -1 + index = 0 + inside_group = False + level = 0 + + while index < len(lines): + line = lines[index] + clean_line = line.strip() + + if clean_line.startswith("group") or clean_line.startswith("partition"): + if count == 1: + group_start = index + inside_group = True + count -= 1 + index += 1 + continue + count -= 1 + if ( + inside_group + and clean_line.startswith("group") + or clean_line.startswith("partition") + ): + level += 1 + if inside_group and clean_line == "end group" or clean_line == "}": + if level == 0: + group_end = index + break + level -= 1 + index += 1 + return group_start, group_end + + +def get_group_text(puml, svg, clickedelement: Literal["group", "partition"]) -> str: + lines = puml.splitlines() + count = group_count(svg, clickedelement) + group_start, _ = find_group_bounds(lines, count) + group_line = lines[group_start] + if group_line.startswith("group"): + return group_line.split(" ", 1)[1] + else: # partition + return group_line.split("partition", 1)[1].split("{", 1)[0].strip() + + +def edit_group(puml, svg, clickedelement, text): + lines = puml.splitlines() + count = group_count(svg, clickedelement) + group_start, group_end = find_group_bounds(lines, count) + if text == "": + del lines[group_start] + del lines[group_end - 1] + else: + if lines[group_start].startswith("group"): + lines[group_start] = f"group {text}" + else: + lines[group_start] = f"partition {text} {{" + return "\n".join(lines) + + +def delete_group(puml, svg, clickedelement): + lines = puml.splitlines() + count = group_count(svg, clickedelement) + group_start, group_end = find_group_bounds(lines, count) + del lines[group_start] + del lines[group_end - 1] + return "\n".join(lines) + + +def get_group_line(puml, svg, clickedelement): + lines = puml.splitlines() + count = group_count(svg, clickedelement) + group_start, group_end = find_group_bounds(lines, count) + return group_start, group_end diff --git a/src/plantuml_gui/if_statements.py b/src/plantuml_gui/if_statements.py new file mode 100644 index 0000000..8546c97 --- /dev/null +++ b/src/plantuml_gui/if_statements.py @@ -0,0 +1,490 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import re +from typing import Literal + +from plantuml_gui.util import checkifwhile # pragma: no cover +from pyquery import PyQuery as Pq + +from .classes import ( + IfElseNode, + PolyElement, + RepeatSwitchNode, + SvgChunk, + TextElement, + find_end, + findelsebounds, +) + + +def svgtochunklistpolygon(svg: str) -> list[SvgChunk]: + chunks = [] + d = Pq(svg) + + polys = d("polygon") + for poly in polys: + poly = Pq(poly) + poly_svg = str(poly) + poly_svg = poly_svg[:-2] + ">" + poly_obj = PolyElement.from_svg(poly_svg) + if poly_obj.is_merge(): + continue # differentiate between merge polygons and statements. + + text_elements = [] + next_elem = poly.next() + + # Unified handling for both 'text' and 'a' elements + while next_elem and next_elem[0].tag in {"text", "a"}: + if next_elem[0].tag == "text": + # Handle text elements as before + element = TextElement.from_svg(next_elem) + next_elem = next_elem.next() + elif next_elem[0].tag == "a": + # Handle 'a' elements similarly to text + link_href = next_elem[0].get("href") + link_text = next_elem[0].find("text").text + + # Create a temporary TextElement for the tag, excluding the link text itself from the label + element = TextElement(label=f"[[{link_href} {link_text}]]") + element.x = float(next_elem[0].find("text").get("x")) + element.y = float(next_elem[0].find("text").get("y")) + next_elem = next_elem.next() + + # Append to the text elements list + text_elements.append(element) + + chunks.append(SvgChunk(object=poly_obj, text_elements=text_elements)) + + return chunks + + +def polychunktotext( + puml: str, svgchunklist: list[SvgChunk], clickedelement: PolyElement +) -> list[str]: + max_x, min_x = None, None + max_y, min_y = None, None + pairs = clickedelement.get_points() + for pair in pairs: + # Convert string values to integers + x = float(pair[0]) + y = float(pair[1]) + + # Update max_x and min_x + if max_x is None or x > max_x: + max_x = x + if min_x is None or x < min_x: + min_x = x + + # Update max_y and min_y + if max_y is None or y > max_y: + max_y = y + if min_y is None or y < min_y: + min_y = y + + lines = puml.splitlines() + texts = [] + + # Containers for the categorized texts + inside_texts = [] + left_texts = [] + right_texts = [] + + for chunk in svgchunklist: + if chunk.object == clickedelement: + # Dictionary to group texts by their y value + y_groups: dict[float, list[str]] = {} + + for text_element in chunk.text_elements: + curr_x = text_element.x + curr_y = text_element.y + + # Check the position of the text_element + if min_x <= curr_x < max_x and min_y < curr_y < max_y: + # Group texts by their y coordinate because embedded links will split a line into multiple svg elements. + if curr_y not in y_groups: + y_groups[curr_y] = [] + y_groups[curr_y].append(text_element.label) + elif curr_x < min_x or curr_y > max_y: + left_texts.append(text_element.label) + elif curr_x >= max_x: + right_texts.append(text_element.label) + + # Combine the grouped texts into single strings for the same y value + for grouped_texts in y_groups.values(): + inside_texts.append(" ".join(grouped_texts)) + + # Combine the categorized texts into the final output + if inside_texts: + texts.append("\n".join(inside_texts)) + if left_texts: + texts.append("\n".join(left_texts)) + if right_texts: + texts.append("\n".join(right_texts)) + + count = polyelementcount(svgchunklist, clickedelement) + start = find_start(lines, count) + end = find_end(lines, start) + if_start, if_end = findifbounds(lines, start) + else_start, else_end = findelsebounds(lines, start) + if if_start == else_start - 1 and end - 1 != else_end: + texts[1], texts[2] = texts[2], texts[1] + return texts + + +def edittextinternalif2( + puml: str, + svgchunklist: list[SvgChunk], + statement: str, + branch1: str, + branch2: str, + clickedelement: PolyElement, +): + count = polyelementcount(svgchunklist, clickedelement) + lines = puml.splitlines() + + start = find_start(lines, count) + end = find_end(lines, start) + + if lines[start].startswith("if"): + if_start, if_end = findifbounds(lines, start) + + if_lines = lines[if_start : if_end + 1] + if_text = "\n".join(if_lines) + if_text = re.sub( + r"if \((.*?)\)", + f"if ({statement})", + if_text, + flags=re.DOTALL, + ) + if_text = re.sub( + r"then \((.*?)\)", + f"then ({branch1})", + if_text, + flags=re.DOTALL, + ) + + if_lines = if_text.replace("\n", "\\n").splitlines() + lines[if_start : if_end + 1] = if_lines + + else_start, else_end = findelsebounds(lines, if_start) + if else_start == -1 and else_end == -1 and branch2 != "": + start = find_start(lines, count) + end = find_end(lines, start) + lines.insert(end, f"else ({branch2})") + + else: + else_lines = lines[else_start : else_end + 1] + else_text = "\n".join(else_lines) + else_text = re.sub( + r"else \((.*?)\)", + f"else ({branch2})", + else_text, + flags=re.DOTALL, + ) + else_lines = else_text.replace("\n", "\\n").splitlines() + lines[else_start : else_end + 1] = else_lines + return "\n".join(lines) + elif lines[start] == "repeat": + repeatwhile_text = lines[end] + repeatwhile_text = re.sub( + r"while \((.*?)\)", + f"while ({statement})", + repeatwhile_text, + flags=re.DOTALL, + ) + repeatwhile_text = re.sub( + r"is \((.*?)\)", + f"is ({branch2})", + repeatwhile_text, + flags=re.DOTALL, + ) + repeatwhile_text = re.sub( + r"not \((.*?)\)", + f"not ({branch1})", + repeatwhile_text, + flags=re.DOTALL, + ) + lines[end] = repeatwhile_text.replace("\n", "\\n") + return "\n".join(lines) + elif lines[start].startswith("switch"): + switch_text = lines[start] + switch_text = re.sub( + r"switch \((.*?)\)", + f"switch ({statement})", + switch_text, + flags=re.DOTALL, + ) + switch_lines = switch_text.splitlines() + switch_text = "\n".join(switch_lines) + lines[start] = switch_text.replace("\n", "\\n") + return "\n".join(lines) + + +def findifbounds(lines, start): + start_if = start + end_if = -1 + index = start + parentheses = 0 + then_found = False + + while index < len(lines): # find index of the correct if statement. + line = lines[index] + clean_line = line.strip() + parentheses += clean_line.count("(") + parentheses -= clean_line.count(")") + + if "then" in clean_line: + then_found = True + + if parentheses == 0 and then_found: + end_if = index + break + index += 1 + return start_if, end_if + + +# def add_note_if(puml: str, svgchunklist: list[SvgChunk], clickedelement: PolyElement): +# count = polyelementcount(svgchunklist, clickedelement) +# lines = puml.splitlines() + +# start_if, end_if = findwholeifbounds(lines, count) +# lines.insert(end_if + 1, "end note") +# lines.insert(end_if + 1, "note") +# lines.insert(end_if + 1, "note right") +# return "\n".join(lines) + + +def deleteif(puml: str, svgchunklist: list[SvgChunk], clickedelement: PolyElement): + count = polyelementcount(svgchunklist, clickedelement) + lines = puml.splitlines() + + start_if = find_start(lines, count) + end_if = find_end(lines, start_if) + if lines[end_if + 1] == "detach": + end_if += 1 + del lines[start_if : end_if + 1] + return "\n".join(lines) + + +def findwholeifbounds(lines: list[str], count: int): + start_if, end_if = -1, -1 + index = 0 + level = 0 + inside_if = False + while index < len(lines): + line = lines[index] + clean_line = line.strip() + + if clean_line.startswith("if") or clean_line == "repeat": + if count == 1 and not inside_if: + start_if = index # found start if index + inside_if = True + continue + count -= 1 + if inside_if and clean_line.startswith("if") or clean_line == "repeat": + level += 1 + if ( + clean_line.startswith("endif") + or clean_line.startswith("repeat while") + or clean_line.startswith("repeatwhile") + ): + level -= 1 + if inside_if and level == 0: + end_if = index + break + index += 1 + + return start_if, end_if + + +def build_tree(lines): + indices = [] + roots = [] + stack = [] + for index, line in enumerate(lines): + line = line.strip() + if line.startswith("if"): + newnode = IfElseNode( + index=index, ifbranch=[], elsebranch=[], inside_ifbranch=True + ) + add_node(roots, stack, newnode) + + if line.startswith("else"): + stack[-1].inside_ifbranch = False + + if line == "repeat" or line.startswith("switch"): + newnode = RepeatSwitchNode(index=index, branch=[]) + add_node(roots, stack, newnode) + if startswith(line, "endif", "repeat while", "repeatwhile", "switch"): + stack.pop() + for node in roots: + node.add_indices(indices, lines) + return indices + + +def add_node(roots, stack, newnode): + if not stack: + roots.append(newnode) + else: + stack[-1].add_node(newnode) + stack.append(newnode) + + +def startswith(line, *args): + return any(line.startswith(arg) for arg in args) + + +def find_start(lines, count): + elements = build_tree(lines) + for index in elements: + if count == 1: + return index + break + count -= 1 + + raise ValueError + + +def get_if_line(puml, svgchunklist, clickedelement): + count = polyelementcount(svgchunklist, clickedelement) + lines = puml.splitlines() + start = find_start(lines, count) + end = find_end(lines, start) + if lines[start].strip() == "repeat": + return end + elif lines[start].strip().startswith("switch"): + return start, end + + else: + else_start, else_end = findelsebounds(lines, start) + return start, else_start, end + + +def get_line_for_adding_into_if( + puml: str, svg: str, clickedelement: PolyElement, where: Literal["left", "right"] +) -> int: + svgchunklist = svgtochunklistpolygon(svg) + count = polyelementcount(svgchunklist, clickedelement) + lines = puml.splitlines() + start = find_start(lines, count) + end = find_end(lines, start) + + if lines[start].startswith("if"): + start_if, end_if = findifbounds(lines, count) + start_else, end_else = findelsebounds(lines, start_if) + + if where == "left": + return end_else + else: + return end + else: # repeat while + if where == "right": + return end + 1 + raise ValueError( + f"get_if_index called with unexpected combination of arguments {clickedelement=} and {where=}" + ) # pragma: no cover + + +def polyelementcount(svgchunklist: list[SvgChunk], clickedelement: PolyElement): + count = 0 + for svgchunk in svgchunklist: # counts occurances of poly chunks (if statements) + if svgchunk.text_elements and not checkifwhile(svgchunk): + count += 1 + if svgchunk.object == clickedelement: + break + return count + + +def check_what_poly(puml, svgchunklist, clickedelement): + count = polyelementcount(svgchunklist, clickedelement) + lines = puml.splitlines() + + start = find_start(lines, count) + return lines[start] + + +def add_backwards(puml, svgchunklist, clickedelement): + count = polyelementcount(svgchunklist, clickedelement) + lines = puml.splitlines() + + start = find_start(lines, count) + end = find_end(lines, start) + index = start + while index < end: + line = lines[index].strip() + if line.startswith("backward"): + return puml + index += 1 + lines.insert(index, "backward:Activity;") + return "\n".join(lines) + + +def check_if_repeat_has_backward(puml, svgchunklist, clickedelement): + count = polyelementcount(svgchunklist, clickedelement) + lines = puml.splitlines() + + start = find_start(lines, count) + end = find_end(lines, start) + index = start + while index < end: + line = lines[index].strip() + if line.startswith("backward"): + return "backward" + index += 1 + return "empty" + + +def detach_if(puml, svgchunklist, clickedelement): + count = polyelementcount(svgchunklist, clickedelement) + lines = puml.splitlines() + start, end = findwholeifbounds(lines, count) + if lines[end + 1] == "detach": + del lines[end + 1] + else: + lines.insert(end + 1, "detach") + return "\n".join(lines) + + +def switch_again(puml, svgchunklist, clickedelement): + count = polyelementcount(svgchunklist, clickedelement) + lines = puml.splitlines() + start = find_start(lines, count) + end = find_end(lines, start) + + condition_label_numbers = [] + for line in lines: + match = re.search(r"case \( condition (\d+)\)", line) + if match: + condition_label_numbers.append(int(match.group(1))) + + # Determine the next condition label number + if condition_label_numbers: + next_label_number = max(condition_label_numbers) + 1 + else: + next_label_number = 1 + + lines.insert(end, ":Activity;") + lines.insert(end, f"case ( condition {next_label_number})") + return "\n".join(lines) diff --git a/src/plantuml_gui/merge.py b/src/plantuml_gui/merge.py new file mode 100644 index 0000000..25ac7c4 --- /dev/null +++ b/src/plantuml_gui/merge.py @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from pyquery import PyQuery as Pq # pragma: no cover + +from .classes import PolyElement + + +def get_index_merge(puml, svg, clickedelement): + count = index_of_clicked_merge(svg, clickedelement) + lines = puml.splitlines() + return find_merge_index(lines, count) + + +def index_of_clicked_merge(svg, clickedelement): + count = 0 + d = Pq(svg) + + polys = d("polygon") + for poly in polys: + poly = Pq(poly) + poly_svg = str(poly) + poly_svg = poly_svg[:-2] + ">" + poly_obj = PolyElement.from_svg(poly_svg) + if ( + poly[0].get("style") == "stroke:#181818;stroke-width:0.5;" + and poly_obj.is_merge() + ): + count += 1 + if poly_svg == clickedelement: + break + return count + + +def find_merge_index(lines, count): + merge_index = -1 + index = 0 + + while index < len(lines): + line = lines[index] + clean_line = line.strip() + + if clean_line in {"endif", "end merge", "repeat", "endswitch"}: + if clean_line == "endif": + if not check_endif_merge( + lines, index + ): # checks if the endif merge exists or not, detach can make it dissapear. + index += 1 + continue + if count == 1: + merge_index = index + break + count -= 1 + index += 1 + return merge_index + + +def check_endif_merge(lines, index): + if ( + lines[index + 1].strip() == "detach" + or lines[index - 1].strip() == "detach" + or lines[index - 1].strip().startswith("else") + or lines[index - 1].strip() in ["stop", "end"] + ): # checks for detach before and after endif line + return False + + while ( + index > 0 + ): # checks for endif before else statements and also works for nested ifs. + level = 0 + if lines[index].strip().startswith("else") and level == 0: + if lines[index - 1].strip() == "detach": + return False + if lines[index].strip() == "endif": + level += 1 + if lines[index].startswith("if"): + level -= 1 + + index -= 1 + + return True diff --git a/src/plantuml_gui/note.py b/src/plantuml_gui/note.py new file mode 100644 index 0000000..6ed7773 --- /dev/null +++ b/src/plantuml_gui/note.py @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from pyquery import PyQuery as Pq # pragma: no cover + + +def note_count(svg, clickedelement): + count = 0 + d = Pq(svg) + + paths = d("path") + for path in paths: + path = Pq(path) + if ( + path[0].get("style") == "pointer-events: none;" + ): # to make sure the path creating the text inside connectors doesnt affect note count + continue + path_svg = str(path) + path_svg = path_svg[:-2] + ">" + next = path.next() + if next[0].tag == "path": # notes have a second path in top corner + count += 1 + if path_svg == clickedelement: + break + return count + + +def find_note_bounds(lines, count): + note_start, note_end = -1, -1 + index = 0 + inside_note = False + + while index < len(lines): + line = lines[index] + clean_line = line.strip() + + if clean_line.startswith("note left") or clean_line.startswith("note right"): + if count == 1: + note_start = index + inside_note = True + count -= 1 + if inside_note and clean_line.startswith("end note"): + note_end = index + break + index += 1 + return note_start, note_end + + +def get_note_text(puml, svg, clickedelement): + lines = puml.splitlines() + count = note_count(svg, clickedelement) + note_start, note_end = find_note_bounds(lines, count) + note_textlines = lines[note_start + 1 : note_end] + return "\n".join(note_textlines) + + +def edit_note(puml, svg, clickedelement, text): + lines = puml.splitlines() + count = note_count(svg, clickedelement) + note_start, note_end = find_note_bounds(lines, count) + if text == "": + del lines[note_start : note_end + 1] + return "\n".join(lines) + new_note_textlines = text.splitlines() + lines[note_start + 1 : note_end] = new_note_textlines + return "\n".join(lines) + + +def delete_note(puml, svg, clickedelement): + lines = puml.splitlines() + count = note_count(svg, clickedelement) + note_start, note_end = find_note_bounds(lines, count) + del lines[note_start : note_end + 1] + return "\n".join(lines) + + +def get_note_line(puml, svg, clickedelement): + lines = puml.splitlines() + count = note_count(svg, clickedelement) + note_start, note_end = find_note_bounds(lines, count) + return note_start, note_end + + +def note_toggle(puml, svg, clickedelement): + lines = puml.splitlines() + count = note_count(svg, clickedelement) + note_start, note_end = find_note_bounds(lines, count) + if lines[note_start] == "note left": + lines[note_start] = "note right" + else: + lines[note_start] = "note left" + return "\n".join(lines) diff --git a/src/plantuml_gui/puml_encoder.py b/src/plantuml_gui/puml_encoder.py new file mode 100644 index 0000000..ea033ad --- /dev/null +++ b/src/plantuml_gui/puml_encoder.py @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import base64 +import string +import zlib + +plantuml_alphabet = ( + string.digits + string.ascii_uppercase + string.ascii_lowercase + "-_" +) +base64_alphabet = string.ascii_uppercase + string.ascii_lowercase + string.digits + "+/" +b64_to_plantuml = bytes.maketrans( + base64_alphabet.encode("utf-8"), plantuml_alphabet.encode("utf-8") +) +plantuml_to_b64 = bytes.maketrans( + plantuml_alphabet.encode("utf-8"), base64_alphabet.encode("utf-8") +) + + +def plantuml_encode(plantuml_text): + """zlib compress the plantuml text and encode it for the plantuml server""" + zlibbed_str = zlib.compress(plantuml_text.encode("utf-8")) + compressed_string = zlibbed_str[2:-4] + return ( + base64.b64encode(compressed_string).translate(b64_to_plantuml).decode("utf-8") + ) + + +def plantuml_decode(plantuml_url): + """decode plantuml encoded url back to plantuml text""" + data = base64.b64decode(plantuml_url.translate(plantuml_to_b64).encode("utf-8")) + dec = zlib.decompressobj() # without check the crc. + header = b"x\x9c" + return dec.decompress(header + data).decode("utf-8") + + +url = "SyfFKj2rKt3CoKnELR1Io4ZDoSa700==" + +print(plantuml_decode(url)) +print(plantuml_encode(plantuml_decode(url))) diff --git a/src/plantuml_gui/render.py b/src/plantuml_gui/render.py new file mode 100644 index 0000000..dbb1ea4 --- /dev/null +++ b/src/plantuml_gui/render.py @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +#!/usr/bin/env python3 +"""Contains the class that converts inline PlantUML code to image links to new image files.""" + +import os +from pathlib import Path +from subprocess import PIPE, run + +from dotenv import load_dotenv + +load_dotenv(Path(__file__).parent.parent.parent / ".env", override=True) + + +def _create_svg_from_uml(uml): + """Create a scalable vector graphic(SVG) from a UML string + :param uml: The input UML text used for creating the image + :return: The content of generated svg + """ + base_command = [ + "java", + "-DPLANTUML_LIMIT_SIZE=16384", + "-jar", + os.environ["PLANTUML_JAR"], + "-pipe", + "-tsvg", # output in svg format + ] + process = run( + base_command, + stdout=PIPE, + stderr=PIPE, + input=bytes(uml, "utf-8"), + check=False, + ) + return process.stdout.decode("utf-8") + + +def _create_png_from_uml(uml): + """Create a PNG image from a UML string + :param uml: The input UML text used for creating the image + :return: The content of generated png + """ + base_command = [ + "java", + "-DPLANTUML_LIMIT_SIZE=16384", + "-jar", + os.environ["PLANTUML_JAR"], + "-pipe", + "-tpng", # output in png format + ] + process = run( + base_command, + stdout=PIPE, + stderr=PIPE, + input=bytes(uml, "utf-8"), + check=False, + ) + return process.stdout diff --git a/src/plantuml_gui/static/Circles-menu-3.gif b/src/plantuml_gui/static/Circles-menu-3.gif new file mode 100644 index 0000000000000000000000000000000000000000..f3d639932bb656d52117dcb1418401d5431ea366 GIT binary patch literal 20218 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

jKQ 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 subjectString.length) { + position = subjectString.length; + } + position -= searchString.length; + var lastIndex = subjectString.indexOf(searchString, position); + return lastIndex !== -1 && lastIndex === position; + }); + } + if (!String.prototype.repeat) { + defineProp(String.prototype, "repeat", function(count) { + var result = ""; + var string = this; + while (count > 0) { + if (count & 1) + result += string; + if ((count >>= 1)) + string += string; + } + return result; + }); + } + if (!String.prototype.includes) { + defineProp(String.prototype, "includes", function(str, position) { + return this.indexOf(str, position) != -1; + }); + } + if (!Object.assign) { + Object.assign = function(target) { + if (target === undefined || target === null) { + throw new TypeError("Cannot convert undefined or null to object"); + } + var output = Object(target); + for (var index = 1; index < arguments.length; index++) { + var source = arguments[index]; + if (source !== undefined && source !== null) { + Object.keys(source).forEach(function(key) { + output[key] = source[key]; + }); + } + } + return output; + }; + } + if (!Object.values) { + Object.values = function(o) { + return Object.keys(o).map(function(k) { + return o[k]; + }); + }; + } + if (!Array.prototype.find) { + defineProp(Array.prototype, "find", function(predicate) { + var len = this.length; + var thisArg = arguments[1]; + for (var k = 0; k < len; k++) { + var kValue = this[k]; + if (predicate.call(thisArg, kValue, k, this)) { + return kValue; + } + } + }); + } + if (!Array.prototype.findIndex) { + defineProp(Array.prototype, "findIndex", function(predicate) { + var len = this.length; + var thisArg = arguments[1]; + for (var k = 0; k < len; k++) { + var kValue = this[k]; + if (predicate.call(thisArg, kValue, k, this)) { + return k; + } + } + }); + } + if (!Array.prototype.includes) { + defineProp(Array.prototype, "includes", function(item, position) { + return this.indexOf(item, position) != -1; + }); + } + if (!Array.prototype.fill) { + defineProp(Array.prototype, "fill", function(value) { + var O = this; + var len = O.length >>> 0; + var start = arguments[1]; + var relativeStart = start >> 0; + var k = relativeStart < 0 ? + Math.max(len + relativeStart, 0) : + Math.min(relativeStart, len); + var end = arguments[2]; + var relativeEnd = end === undefined ? len : end >> 0; + var final = relativeEnd < 0 ? + Math.max(len + relativeEnd, 0) : + Math.min(relativeEnd, len); + while (k < final) { + O[k] = value; + k++; + } + return O; + }); + } + if (!Array.of) { + defineProp(Array, "of", function() { + return Array.prototype.slice.call(arguments); + }); + } + +}); + +define("ace/lib/fixoldbrowsers", ["require", "exports", "module", "ace/lib/es6-shim"], function(require, exports, module) { // vim:set ts=4 sts=4 sw=4 st: + "use strict"; + require("./es6-shim"); + +}); + +define("ace/lib/deep_copy", ["require", "exports", "module"], function(require, exports, module) { + exports.deepCopy = function deepCopy(obj) { + if (typeof obj !== "object" || !obj) + return obj; + var copy; + if (Array.isArray(obj)) { + copy = []; + for (var key = 0; key < obj.length; key++) { + copy[key] = deepCopy(obj[key]); + } + return copy; + } + if (Object.prototype.toString.call(obj) !== "[object Object]") + return obj; + copy = {}; + for (var key in obj) + copy[key] = deepCopy(obj[key]); + return copy; + }; + +}); + +define("ace/lib/lang", ["require", "exports", "module", "ace/lib/deep_copy"], function(require, exports, module) { + "use strict"; + exports.last = function(a) { + return a[a.length - 1]; + }; + exports.stringReverse = function(string) { + return string.split("").reverse().join(""); + }; + exports.stringRepeat = function(string, count) { + var result = ''; + while (count > 0) { + if (count & 1) + result += string; + if (count >>= 1) + string += string; + } + return result; + }; + var trimBeginRegexp = /^\s\s*/; + var trimEndRegexp = /\s\s*$/; + exports.stringTrimLeft = function(string) { + return string.replace(trimBeginRegexp, ''); + }; + exports.stringTrimRight = function(string) { + return string.replace(trimEndRegexp, ''); + }; + exports.copyObject = function(obj) { + var copy = {}; + for (var key in obj) { + copy[key] = obj[key]; + } + return copy; + }; + exports.copyArray = function(array) { + var copy = []; + for (var i = 0, l = array.length; i < l; i++) { + if (array[i] && typeof array[i] == "object") + copy[i] = this.copyObject(array[i]); + else + copy[i] = array[i]; + } + return copy; + }; + exports.deepCopy = require("./deep_copy").deepCopy; + exports.arrayToMap = function(arr) { + var map = {}; + for (var i = 0; i < arr.length; i++) { + map[arr[i]] = 1; + } + return map; + }; + exports.createMap = function(props) { + var map = Object.create(null); + for (var i in props) { + map[i] = props[i]; + } + return map; + }; + exports.arrayRemove = function(array, value) { + for (var i = 0; i <= array.length; i++) { + if (value === array[i]) { + array.splice(i, 1); + } + } + }; + exports.escapeRegExp = function(str) { + return str.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); + }; + exports.escapeHTML = function(str) { + return ("" + str).replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/ 0xffff ? 2 : 1; + }; + +}); + +define("ace/lib/useragent", ["require", "exports", "module"], function(require, exports, module) { + "use strict"; + exports.OS = { + LINUX: "LINUX", + MAC: "MAC", + WINDOWS: "WINDOWS" + }; + exports.getOS = function() { + if (exports.isMac) { + return exports.OS.MAC; + } else if (exports.isLinux) { + return exports.OS.LINUX; + } else { + return exports.OS.WINDOWS; + } + }; + var _navigator = typeof navigator == "object" ? navigator : {}; + var os = (/mac|win|linux/i.exec(_navigator.platform) || ["other"])[0].toLowerCase(); + var ua = _navigator.userAgent || ""; + var appName = _navigator.appName || ""; + exports.isWin = (os == "win"); + exports.isMac = (os == "mac"); + exports.isLinux = (os == "linux"); + exports.isIE = + (appName == "Microsoft Internet Explorer" || appName.indexOf("MSAppHost") >= 0) ? + parseFloat((ua.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/) || [])[1]) : + parseFloat((ua.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/) || [])[1]); // for ie + exports.isOldIE = exports.isIE && exports.isIE < 9; + exports.isGecko = exports.isMozilla = ua.match(/ Gecko\/\d+/); + exports.isOpera = typeof opera == "object" && Object.prototype.toString.call(window["opera"]) == "[object Opera]"; + exports.isWebKit = parseFloat(ua.split("WebKit/")[1]) || undefined; + exports.isChrome = parseFloat(ua.split(" Chrome/")[1]) || undefined; + exports.isSafari = parseFloat(ua.split(" Safari/")[1]) && !exports.isChrome || undefined; + exports.isEdge = parseFloat(ua.split(" Edge/")[1]) || undefined; + exports.isAIR = ua.indexOf("AdobeAIR") >= 0; + exports.isAndroid = ua.indexOf("Android") >= 0; + exports.isChromeOS = ua.indexOf(" CrOS ") >= 0; + exports.isIOS = /iPad|iPhone|iPod/.test(ua) && !window["MSStream"]; + if (exports.isIOS) + exports.isMac = true; + exports.isMobile = exports.isIOS || exports.isAndroid; + +}); + +define("ace/lib/dom", ["require", "exports", "module", "ace/lib/useragent"], function(require, exports, module) { + "use strict"; + var useragent = require("./useragent"); + var XHTML_NS = "http://www.w3.org/1999/xhtml"; + exports.buildDom = function buildDom(arr, parent, refs) { + if (typeof arr == "string" && arr) { + var txt = document.createTextNode(arr); + if (parent) + parent.appendChild(txt); + return txt; + } + if (!Array.isArray(arr)) { + if (arr && arr.appendChild && parent) + parent.appendChild(arr); + return arr; + } + if (typeof arr[0] != "string" || !arr[0]) { + var els = []; + for (var i = 0; i < arr.length; i++) { + var ch = buildDom(arr[i], parent, refs); + ch && els.push(ch); + } + return els; + } + var el = document.createElement(arr[0]); + var options = arr[1]; + var childIndex = 1; + if (options && typeof options == "object" && !Array.isArray(options)) + childIndex = 2; + for (var i = childIndex; i < arr.length; i++) + buildDom(arr[i], el, refs); + if (childIndex == 2) { + Object.keys(options).forEach(function(n) { + var val = options[n]; + if (n === "class") { + el.className = Array.isArray(val) ? val.join(" ") : val; + } else if (typeof val == "function" || n == "value" || n[0] == "$") { + el[n] = val; + } else if (n === "ref") { + if (refs) + refs[val] = el; + } else if (n === "style") { + if (typeof val == "string") + el.style.cssText = val; + } else if (val != null) { + el.setAttribute(n, val); + } + }); + } + if (parent) + parent.appendChild(el); + return el; + }; + exports.getDocumentHead = function(doc) { + if (!doc) + doc = document; + return doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement; + }; + exports.createElement = function(tag, ns) { + return document.createElementNS ? + document.createElementNS(ns || XHTML_NS, tag) : + document.createElement(tag); + }; + exports.removeChildren = function(element) { + element.innerHTML = ""; + }; + exports.createTextNode = function(textContent, element) { + var doc = element ? element.ownerDocument : document; + return doc.createTextNode(textContent); + }; + exports.createFragment = function(element) { + var doc = element ? element.ownerDocument : document; + return doc.createDocumentFragment(); + }; + exports.hasCssClass = function(el, name) { + var classes = (el.className + "").split(/\s+/g); + return classes.indexOf(name) !== -1; + }; + exports.addCssClass = function(el, name) { + if (!exports.hasCssClass(el, name)) { + el.className += " " + name; + } + }; + exports.removeCssClass = function(el, name) { + var classes = el.className.split(/\s+/g); + while (true) { + var index = classes.indexOf(name); + if (index == -1) { + break; + } + classes.splice(index, 1); + } + el.className = classes.join(" "); + }; + exports.toggleCssClass = function(el, name) { + var classes = el.className.split(/\s+/g), + add = true; + while (true) { + var index = classes.indexOf(name); + if (index == -1) { + break; + } + add = false; + classes.splice(index, 1); + } + if (add) + classes.push(name); + el.className = classes.join(" "); + return add; + }; + exports.setCssClass = function(node, className, include) { + if (include) { + exports.addCssClass(node, className); + } else { + exports.removeCssClass(node, className); + } + }; + exports.hasCssString = function(id, doc) { + var index = 0, + sheets; + doc = doc || document; + if ((sheets = doc.querySelectorAll("style"))) { + while (index < sheets.length) { + if (sheets[index++].id === id) { + return true; + } + } + } + }; + exports.removeElementById = function(id, doc) { + doc = doc || document; + if (doc.getElementById(id)) { + doc.getElementById(id).remove(); + } + }; + var strictCSP; + var cssCache = []; + exports.useStrictCSP = function(value) { + strictCSP = value; + if (value == false) + insertPendingStyles(); + else if (!cssCache) + cssCache = []; + }; + + function insertPendingStyles() { + var cache = cssCache; + cssCache = null; + cache && cache.forEach(function(item) { + importCssString(item[0], item[1]); + }); + } + + function importCssString(cssText, id, target) { + if (typeof document == "undefined") + return; + if (cssCache) { + if (target) { + insertPendingStyles(); + } else if (target === false) { + return cssCache.push([cssText, id]); + } + } + if (strictCSP) + return; + var container = target; + if (!target || !target.getRootNode) { + container = document; + } else { + container = target.getRootNode(); + if (!container || container == target) + container = document; + } + var doc = container.ownerDocument || container; + if (id && exports.hasCssString(id, container)) + return null; + if (id) + cssText += "\n/*# sourceURL=ace/css/" + id + " */"; + var style = exports.createElement("style"); + style.appendChild(doc.createTextNode(cssText)); + if (id) + style.id = id; + if (container == doc) + container = exports.getDocumentHead(doc); + container.insertBefore(style, container.firstChild); + } + exports.importCssString = importCssString; + exports.importCssStylsheet = function(uri, doc) { + exports.buildDom(["link", { + rel: "stylesheet", + href: uri + }], exports.getDocumentHead(doc)); + }; + exports.scrollbarWidth = function(doc) { + var inner = exports.createElement("ace_inner"); + inner.style.width = "100%"; + inner.style.minWidth = "0px"; + inner.style.height = "200px"; + inner.style.display = "block"; + var outer = exports.createElement("ace_outer"); + var style = outer.style; + style.position = "absolute"; + style.left = "-10000px"; + style.overflow = "hidden"; + style.width = "200px"; + style.minWidth = "0px"; + style.height = "150px"; + style.display = "block"; + outer.appendChild(inner); + var body = (doc && doc.documentElement) || (document && document.documentElement); + if (!body) + return 0; + body.appendChild(outer); + var noScrollbar = inner.offsetWidth; + style.overflow = "scroll"; + var withScrollbar = inner.offsetWidth; + if (noScrollbar === withScrollbar) { + withScrollbar = outer.clientWidth; + } + body.removeChild(outer); + return noScrollbar - withScrollbar; + }; + exports.computedStyle = function(element, style) { + return window.getComputedStyle(element, "") || {}; + }; + exports.setStyle = function(styles, property, value) { + if (styles[property] !== value) { + styles[property] = value; + } + }; + exports.HAS_CSS_ANIMATION = false; + exports.HAS_CSS_TRANSFORMS = false; + exports.HI_DPI = useragent.isWin ? + typeof window !== "undefined" && window.devicePixelRatio >= 1.5 : + true; + if (useragent.isChromeOS) + exports.HI_DPI = false; + if (typeof document !== "undefined") { + var div = document.createElement("div"); + if (exports.HI_DPI && div.style.transform !== undefined) + exports.HAS_CSS_TRANSFORMS = true; + if (!useragent.isEdge && typeof div.style.animationName !== "undefined") + exports.HAS_CSS_ANIMATION = true; + div = null; + } + if (exports.HAS_CSS_TRANSFORMS) { + exports.translate = function(element, tx, ty) { + element.style.transform = "translate(" + Math.round(tx) + "px, " + Math.round(ty) + "px)"; + }; + } else { + exports.translate = function(element, tx, ty) { + element.style.top = Math.round(ty) + "px"; + element.style.left = Math.round(tx) + "px"; + }; + } + +}); + +define("ace/lib/net", ["require", "exports", "module", "ace/lib/dom"], function(require, exports, module) { + /* + * based on code from: + * + * @license RequireJS text 0.25.0 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/requirejs for details + */ + "use strict"; + var dom = require("./dom"); + exports.get = function(url, callback) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + callback(xhr.responseText); + } + }; + xhr.send(null); + }; + exports.loadScript = function(path, callback) { + var head = dom.getDocumentHead(); + var s = document.createElement('script'); + s.src = path; + head.appendChild(s); + s.onload = s.onreadystatechange = function(_, isAbort) { + if (isAbort || !s.readyState || s.readyState == "loaded" || s.readyState == "complete") { + s = s.onload = s.onreadystatechange = null; + if (!isAbort) + callback(); + } + }; + }; + exports.qualifyURL = function(url) { + var a = document.createElement('a'); + a.href = url; + return a.href; + }; + +}); + +define("ace/lib/oop", ["require", "exports", "module"], function(require, exports, module) { + "use strict"; + exports.inherits = function(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; + exports.mixin = function(obj, mixin) { + for (var key in mixin) { + obj[key] = mixin[key]; + } + return obj; + }; + exports.implement = function(proto, mixin) { + exports.mixin(proto, mixin); + }; + +}); + +define("ace/lib/event_emitter", ["require", "exports", "module"], function(require, exports, module) { + "use strict"; + var EventEmitter = {}; + var stopPropagation = function() { + this.propagationStopped = true; + }; + var preventDefault = function() { + this.defaultPrevented = true; + }; + EventEmitter._emit = + EventEmitter._dispatchEvent = function(eventName, e) { + this._eventRegistry || (this._eventRegistry = {}); + this._defaultHandlers || (this._defaultHandlers = {}); + var listeners = this._eventRegistry[eventName] || []; + var defaultHandler = this._defaultHandlers[eventName]; + if (!listeners.length && !defaultHandler) + return; + if (typeof e != "object" || !e) + e = {}; + if (!e.type) + e.type = eventName; + if (!e.stopPropagation) + e.stopPropagation = stopPropagation; + if (!e.preventDefault) + e.preventDefault = preventDefault; + listeners = listeners.slice(); + for (var i = 0; i < listeners.length; i++) { + listeners[i](e, this); + if (e.propagationStopped) + break; + } + if (defaultHandler && !e.defaultPrevented) + return defaultHandler(e, this); + }; + EventEmitter._signal = function(eventName, e) { + var listeners = (this._eventRegistry || {})[eventName]; + if (!listeners) + return; + listeners = listeners.slice(); + for (var i = 0; i < listeners.length; i++) + listeners[i](e, this); + }; + EventEmitter.once = function(eventName, callback) { + var _self = this; + this.on(eventName, function newCallback() { + _self.off(eventName, newCallback); + callback.apply(null, arguments); + }); + if (!callback) { + return new Promise(function(resolve) { + callback = resolve; + }); + } + }; + EventEmitter.setDefaultHandler = function(eventName, callback) { + var handlers = this._defaultHandlers; + if (!handlers) + handlers = this._defaultHandlers = { + _disabled_: {} + }; + if (handlers[eventName]) { + var old = handlers[eventName]; + var disabled = handlers._disabled_[eventName]; + if (!disabled) + handlers._disabled_[eventName] = disabled = []; + disabled.push(old); + var i = disabled.indexOf(callback); + if (i != -1) + disabled.splice(i, 1); + } + handlers[eventName] = callback; + }; + EventEmitter.removeDefaultHandler = function(eventName, callback) { + var handlers = this._defaultHandlers; + if (!handlers) + return; + var disabled = handlers._disabled_[eventName]; + if (handlers[eventName] == callback) { + if (disabled) + this.setDefaultHandler(eventName, disabled.pop()); + } else if (disabled) { + var i = disabled.indexOf(callback); + if (i != -1) + disabled.splice(i, 1); + } + }; + EventEmitter.on = + EventEmitter.addEventListener = function(eventName, callback, capturing) { + this._eventRegistry = this._eventRegistry || {}; + var listeners = this._eventRegistry[eventName]; + if (!listeners) + listeners = this._eventRegistry[eventName] = []; + if (listeners.indexOf(callback) == -1) + listeners[capturing ? "unshift" : "push"](callback); + return callback; + }; + EventEmitter.off = + EventEmitter.removeListener = + EventEmitter.removeEventListener = function(eventName, callback) { + this._eventRegistry = this._eventRegistry || {}; + var listeners = this._eventRegistry[eventName]; + if (!listeners) + return; + var index = listeners.indexOf(callback); + if (index !== -1) + listeners.splice(index, 1); + }; + EventEmitter.removeAllListeners = function(eventName) { + if (!eventName) + this._eventRegistry = this._defaultHandlers = undefined; + if (this._eventRegistry) + this._eventRegistry[eventName] = undefined; + if (this._defaultHandlers) + this._defaultHandlers[eventName] = undefined; + }; + exports.EventEmitter = EventEmitter; + +}); + +define("ace/lib/report_error", ["require", "exports", "module"], function(require, exports, module) { + exports.reportError = function reportError(msg, data) { + var e = new Error(msg); + e["data"] = data; + if (typeof console == "object" && console.error) + console.error(e); + setTimeout(function() { + throw e; + }); + }; + +}); + +define("ace/lib/default_english_messages", ["require", "exports", "module"], function(require, exports, module) { + var defaultEnglishMessages = { + "autocomplete.popup.aria-roledescription": "Autocomplete suggestions", + "autocomplete.popup.aria-label": "Autocomplete suggestions", + "autocomplete.popup.item.aria-roledescription": "item", + "autocomplete.loading": "Loading...", + "editor.scroller.aria-roledescription": "editor", + "editor.scroller.aria-label": "Editor content, press Enter to start editing, press Escape to exit", + "editor.gutter.aria-roledescription": "editor", + "editor.gutter.aria-label": "Editor gutter, press Enter to interact with controls using arrow keys, press Escape to exit", + "error-marker.good-state": "Looks good!", + "prompt.recently-used": "Recently used", + "prompt.other-commands": "Other commands", + "prompt.no-matching-commands": "No matching commands", + "search-box.find.placeholder": "Search for", + "search-box.find-all.text": "All", + "search-box.replace.placeholder": "Replace with", + "search-box.replace-next.text": "Replace", + "search-box.replace-all.text": "All", + "search-box.toggle-replace.title": "Toggle Replace mode", + "search-box.toggle-regexp.title": "RegExp Search", + "search-box.toggle-case.title": "CaseSensitive Search", + "search-box.toggle-whole-word.title": "Whole Word Search", + "search-box.toggle-in-selection.title": "Search In Selection", + "search-box.search-counter": "$0 of $1", + "text-input.aria-roledescription": "editor", + "text-input.aria-label": "Cursor at row $0", + "gutter.code-folding.range.aria-label": "Toggle code folding, rows $0 through $1", + "gutter.code-folding.closed.aria-label": "Toggle code folding, rows $0 through $1", + "gutter.code-folding.open.aria-label": "Toggle code folding, row $0", + "gutter.code-folding.closed.title": "Unfold code", + "gutter.code-folding.open.title": "Fold code", + "gutter.annotation.aria-label.error": "Error, read annotations row $0", + "gutter.annotation.aria-label.warning": "Warning, read annotations row $0", + "gutter.annotation.aria-label.info": "Info, read annotations row $0", + "inline-fold.closed.title": "Unfold code", + "gutter-tooltip.aria-label.error.singular": "error", + "gutter-tooltip.aria-label.error.plural": "errors", + "gutter-tooltip.aria-label.warning.singular": "warning", + "gutter-tooltip.aria-label.warning.plural": "warnings", + "gutter-tooltip.aria-label.info.singular": "information message", + "gutter-tooltip.aria-label.info.plural": "information messages" + }; + exports.defaultEnglishMessages = defaultEnglishMessages; + +}); + +define("ace/lib/app_config", ["require", "exports", "module", "ace/lib/oop", "ace/lib/event_emitter", "ace/lib/report_error", "ace/lib/default_english_messages"], function(require, exports, module) { + "no use strict"; + var oop = require("./oop"); + var EventEmitter = require("./event_emitter").EventEmitter; + var reportError = require("./report_error").reportError; + var defaultEnglishMessages = require("./default_english_messages").defaultEnglishMessages; + var optionsProvider = { + setOptions: function(optList) { + Object.keys(optList).forEach(function(key) { + this.setOption(key, optList[key]); + }, this); + }, + getOptions: function(optionNames) { + var result = {}; + if (!optionNames) { + var options = this.$options; + optionNames = Object.keys(options).filter(function(key) { + return !options[key].hidden; + }); + } else if (!Array.isArray(optionNames)) { + result = optionNames; + optionNames = Object.keys(result); + } + optionNames.forEach(function(key) { + result[key] = this.getOption(key); + }, this); + return result; + }, + setOption: function(name, value) { + if (this["$" + name] === value) + return; + var opt = this.$options[name]; + if (!opt) { + return warn('misspelled option "' + name + '"'); + } + if (opt.forwardTo) + return this[opt.forwardTo] && this[opt.forwardTo].setOption(name, value); + if (!opt.handlesSet) + this["$" + name] = value; + if (opt && opt.set) + opt.set.call(this, value); + }, + getOption: function(name) { + var opt = this.$options[name]; + if (!opt) { + return warn('misspelled option "' + name + '"'); + } + if (opt.forwardTo) + return this[opt.forwardTo] && this[opt.forwardTo].getOption(name); + return opt && opt.get ? opt.get.call(this) : this["$" + name]; + } + }; + + function warn(message) { + if (typeof console != "undefined" && console.warn) + console.warn.apply(console, arguments); + } + var messages; + var nlsPlaceholders; + var AppConfig = /** @class */ (function() { + function AppConfig() { + this.$defaultOptions = {}; + messages = defaultEnglishMessages; + nlsPlaceholders = "dollarSigns"; + } + AppConfig.prototype.defineOptions = function(obj, path, options) { + if (!obj.$options) + this.$defaultOptions[path] = obj.$options = {}; + Object.keys(options).forEach(function(key) { + var opt = options[key]; + if (typeof opt == "string") + opt = { + forwardTo: opt + }; + opt.name || (opt.name = key); + obj.$options[opt.name] = opt; + if ("initialValue" in opt) + obj["$" + opt.name] = opt.initialValue; + }); + oop.implement(obj, optionsProvider); + return this; + }; + AppConfig.prototype.resetOptions = function(obj) { + Object.keys(obj.$options).forEach(function(key) { + var opt = obj.$options[key]; + if ("value" in opt) + obj.setOption(key, opt.value); + }); + }; + AppConfig.prototype.setDefaultValue = function(path, name, value) { + if (!path) { + for (path in this.$defaultOptions) + if (this.$defaultOptions[path][name]) + break; + if (!this.$defaultOptions[path][name]) + return false; + } + var opts = this.$defaultOptions[path] || (this.$defaultOptions[path] = {}); + if (opts[name]) { + if (opts.forwardTo) + this.setDefaultValue(opts.forwardTo, name, value); + else + opts[name].value = value; + } + }; + AppConfig.prototype.setDefaultValues = function(path, optionHash) { + Object.keys(optionHash).forEach(function(key) { + this.setDefaultValue(path, key, optionHash[key]); + }, this); + }; + AppConfig.prototype.setMessages = function(value, options) { + messages = value; + if (options && options.placeholders) { + nlsPlaceholders = options.placeholders; + } + }; + AppConfig.prototype.nls = function(key, defaultString, params) { + if (!messages[key]) { + warn("No message found for the key '" + key + "' in the provided messages, trying to find a translation for the default string '" + defaultString + "'."); + if (!messages[defaultString]) { + warn("No message found for the default string '" + defaultString + "' in the provided messages. Falling back to the default English message."); + } + } + var translated = messages[key] || messages[defaultString] || defaultString; + if (params) { + if (nlsPlaceholders === "dollarSigns") { + translated = translated.replace(/\$(\$|[\d]+)/g, function(_, dollarMatch) { + if (dollarMatch == "$") + return "$"; + return params[dollarMatch]; + }); + } + if (nlsPlaceholders === "curlyBrackets") { + translated = translated.replace(/\{([^\}]+)\}/g, function(_, curlyBracketMatch) { + return params[curlyBracketMatch]; + }); + } + } + return translated; + }; + return AppConfig; + }()); + AppConfig.prototype.warn = warn; + AppConfig.prototype.reportError = reportError; + oop.implement(AppConfig.prototype, EventEmitter); + exports.AppConfig = AppConfig; + +}); + +define("ace/theme/textmate-css", ["require", "exports", "module"], function(require, exports, module) { + module.exports = ".ace-tm .ace_gutter {\n background: #f0f0f0;\n color: #333;\n}\n\n.ace-tm .ace_print-margin {\n width: 1px;\n background: #e8e8e8;\n}\n\n.ace-tm .ace_fold {\n background-color: #6B72E6;\n}\n\n.ace-tm {\n background-color: #FFFFFF;\n color: black;\n}\n\n.ace-tm .ace_cursor {\n color: black;\n}\n \n.ace-tm .ace_invisible {\n color: rgb(191, 191, 191);\n}\n\n.ace-tm .ace_storage,\n.ace-tm .ace_keyword {\n color: blue;\n}\n\n.ace-tm .ace_constant {\n color: rgb(197, 6, 11);\n}\n\n.ace-tm .ace_constant.ace_buildin {\n color: rgb(88, 72, 246);\n}\n\n.ace-tm .ace_constant.ace_language {\n color: rgb(88, 92, 246);\n}\n\n.ace-tm .ace_constant.ace_library {\n color: rgb(6, 150, 14);\n}\n\n.ace-tm .ace_invalid {\n background-color: rgba(255, 0, 0, 0.1);\n color: red;\n}\n\n.ace-tm .ace_support.ace_function {\n color: rgb(60, 76, 114);\n}\n\n.ace-tm .ace_support.ace_constant {\n color: rgb(6, 150, 14);\n}\n\n.ace-tm .ace_support.ace_type,\n.ace-tm .ace_support.ace_class {\n color: rgb(109, 121, 222);\n}\n\n.ace-tm .ace_keyword.ace_operator {\n color: rgb(104, 118, 135);\n}\n\n.ace-tm .ace_string {\n color: rgb(3, 106, 7);\n}\n\n.ace-tm .ace_comment {\n color: rgb(76, 136, 107);\n}\n\n.ace-tm .ace_comment.ace_doc {\n color: rgb(0, 102, 255);\n}\n\n.ace-tm .ace_comment.ace_doc.ace_tag {\n color: rgb(128, 159, 191);\n}\n\n.ace-tm .ace_constant.ace_numeric {\n color: rgb(0, 0, 205);\n}\n\n.ace-tm .ace_variable {\n color: rgb(49, 132, 149);\n}\n\n.ace-tm .ace_xml-pe {\n color: rgb(104, 104, 91);\n}\n\n.ace-tm .ace_entity.ace_name.ace_function {\n color: #0000A2;\n}\n\n\n.ace-tm .ace_heading {\n color: rgb(12, 7, 255);\n}\n\n.ace-tm .ace_list {\n color:rgb(185, 6, 144);\n}\n\n.ace-tm .ace_meta.ace_tag {\n color:rgb(0, 22, 142);\n}\n\n.ace-tm .ace_string.ace_regex {\n color: rgb(255, 0, 0)\n}\n\n.ace-tm .ace_marker-layer .ace_selection {\n background: rgb(181, 213, 255);\n}\n.ace-tm.ace_multiselect .ace_selection.ace_start {\n box-shadow: 0 0 3px 0px white;\n}\n.ace-tm .ace_marker-layer .ace_step {\n background: rgb(252, 255, 0);\n}\n\n.ace-tm .ace_marker-layer .ace_stack {\n background: rgb(164, 229, 101);\n}\n\n.ace-tm .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid rgb(192, 192, 192);\n}\n\n.ace-tm .ace_marker-layer .ace_active-line {\n background: rgba(0, 0, 0, 0.07);\n}\n\n.ace-tm .ace_gutter-active-line {\n background-color : #dcdcdc;\n}\n\n.ace-tm .ace_marker-layer .ace_selected-word {\n background: rgb(250, 250, 255);\n border: 1px solid rgb(200, 200, 250);\n}\n\n.ace-tm .ace_indent-guide {\n background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\n}\n\n.ace-tm .ace_indent-guide-active {\n background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAZSURBVHjaYvj///9/hivKyv8BAAAA//8DACLqBhbvk+/eAAAAAElFTkSuQmCC\") right repeat-y;\n}\n"; + +}); + +define("ace/theme/textmate", ["require", "exports", "module", "ace/theme/textmate-css", "ace/lib/dom"], function(require, exports, module) { + "use strict"; + exports.isDark = false; + exports.cssClass = "ace-tm"; + exports.cssText = require("./textmate-css"); + exports.$id = "ace/theme/textmate"; + var dom = require("../lib/dom"); + dom.importCssString(exports.cssText, exports.cssClass, false); + +}); + +define("ace/config", ["require", "exports", "module", "ace/lib/lang", "ace/lib/net", "ace/lib/dom", "ace/lib/app_config", "ace/theme/textmate"], function(require, exports, module) { + "no use strict"; + var lang = require("./lib/lang"); + var net = require("./lib/net"); + var dom = require("./lib/dom"); + var AppConfig = require("./lib/app_config").AppConfig; + module.exports = exports = new AppConfig(); + var options = { + packaged: false, + workerPath: null, + modePath: null, + themePath: null, + basePath: "", + suffix: ".js", + $moduleUrls: {}, + loadWorkerFromBlob: true, + sharedPopups: false, + useStrictCSP: null + }; + exports.get = function(key) { + if (!options.hasOwnProperty(key)) + throw new Error("Unknown config key: " + key); + return options[key]; + }; + exports.set = function(key, value) { + if (options.hasOwnProperty(key)) + options[key] = value; + else if (this.setDefaultValue("", key, value) == false) + throw new Error("Unknown config key: " + key); + if (key == "useStrictCSP") + dom.useStrictCSP(value); + }; + exports.all = function() { + return lang.copyObject(options); + }; + exports.$modes = {}; + exports.moduleUrl = function(name, component) { + if (options.$moduleUrls[name]) + return options.$moduleUrls[name]; + var parts = name.split("/"); + component = component || parts[parts.length - 2] || ""; + var sep = component == "snippets" ? "/" : "-"; + var base = parts[parts.length - 1]; + if (component == "worker" && sep == "-") { + var re = new RegExp("^" + component + "[\\-_]|[\\-_]" + component + "$", "g"); + base = base.replace(re, ""); + } + if ((!base || base == component) && parts.length > 1) + base = parts[parts.length - 2]; + var path = options[component + "Path"]; + if (path == null) { + path = options.basePath; + } else if (sep == "/") { + component = sep = ""; + } + if (path && path.slice(-1) != "/") + path += "/"; + return path + component + sep + base + this.get("suffix"); + }; + exports.setModuleUrl = function(name, subst) { + return options.$moduleUrls[name] = subst; + }; + var loader = function(moduleName, cb) { + if (moduleName === "ace/theme/textmate" || moduleName === "./theme/textmate") + return cb(null, require("./theme/textmate")); + if (customLoader) + return customLoader(moduleName, cb); + console.error("loader is not configured"); + }; + var customLoader; + exports.setLoader = function(cb) { + customLoader = cb; + }; + exports.dynamicModules = Object.create(null); + exports.$loading = {}; + exports.$loaded = {}; + exports.loadModule = function(moduleId, onLoad) { + var loadedModule; + if (Array.isArray(moduleId)) { + var moduleType = moduleId[0]; + var moduleName = moduleId[1]; + } else if (typeof moduleId == "string") { + var moduleName = moduleId; + } + var load = function(module) { + if (module && !exports.$loading[moduleName]) + return onLoad && onLoad(module); + if (!exports.$loading[moduleName]) + exports.$loading[moduleName] = []; + exports.$loading[moduleName].push(onLoad); + if (exports.$loading[moduleName].length > 1) + return; + var afterLoad = function() { + loader(moduleName, function(err, module) { + if (module) + exports.$loaded[moduleName] = module; + exports._emit("load.module", { + name: moduleName, + module: module + }); + var listeners = exports.$loading[moduleName]; + exports.$loading[moduleName] = null; + listeners.forEach(function(onLoad) { + onLoad && onLoad(module); + }); + }); + }; + if (!exports.get("packaged")) + return afterLoad(); + net.loadScript(exports.moduleUrl(moduleName, moduleType), afterLoad); + reportErrorIfPathIsNotConfigured(); + }; + if (exports.dynamicModules[moduleName]) { + exports.dynamicModules[moduleName]().then(function(module) { + if (module.default) { + load(module.default); + } else { + load(module); + } + }); + } else { + try { + loadedModule = this.$require(moduleName); + } catch (e) {} + load(loadedModule || exports.$loaded[moduleName]); + } + }; + exports.$require = function(moduleName) { + if (typeof module["require"] == "function") { + var req = "require"; + return module[req](moduleName); + } + }; + exports.setModuleLoader = function(moduleName, onLoad) { + exports.dynamicModules[moduleName] = onLoad; + }; + var reportErrorIfPathIsNotConfigured = function() { + if (!options.basePath && !options.workerPath && + !options.modePath && !options.themePath && + !Object.keys(options.$moduleUrls).length) { + console.error("Unable to infer path to ace from script src,", "use ace.config.set('basePath', 'path') to enable dynamic loading of modes and themes", "or with webpack use ace/webpack-resolver"); + reportErrorIfPathIsNotConfigured = function() {}; + } + }; + exports.version = "1.35.4"; + +}); + +define("ace/loader_build", ["require", "exports", "module", "ace/lib/fixoldbrowsers", "ace/config"], function(require, exports, module) { + "use strict"; + + require("./lib/fixoldbrowsers"); + var config = require("./config"); + config.setLoader(function(moduleName, cb) { + require([moduleName], function(module) { + cb(null, module); + }); + }); + + var global = (function() { + return this || typeof window != "undefined" && window; + })(); + + module.exports = function(ace) { + config.init = init; + config.$require = require; + ace.require = require; + + if (typeof define === "function") + ace.define = define; + }; + init(true); + + function init(packaged) { + + if (!global || !global.document) + return; + + config.set("packaged", packaged || require.packaged || module.packaged || (global.define && define.packaged)); + + var scriptOptions = {}; + var scriptUrl = ""; + var currentScript = (document.currentScript || document._currentScript); // native or polyfill + var currentDocument = currentScript && currentScript.ownerDocument || document; + + if (currentScript && currentScript.src) { + scriptUrl = currentScript.src.split(/[?#]/)[0].split("/").slice(0, -1).join("/") || ""; + } + + var scripts = currentDocument.getElementsByTagName("script"); + for (var i = 0; i < scripts.length; i++) { + var script = scripts[i]; + + var src = script.src || script.getAttribute("src"); + if (!src) + continue; + + var attributes = script.attributes; + for (var j = 0, l = attributes.length; j < l; j++) { + var attr = attributes[j]; + if (attr.name.indexOf("data-ace-") === 0) { + scriptOptions[deHyphenate(attr.name.replace(/^data-ace-/, ""))] = attr.value; + } + } + + var m = src.match(/^(.*)\/ace([\-.]\w+)?\.js(\?|$)/); + if (m) + scriptUrl = m[1]; + } + + if (scriptUrl) { + scriptOptions.base = scriptOptions.base || scriptUrl; + scriptOptions.packaged = true; + } + + scriptOptions.basePath = scriptOptions.base; + scriptOptions.workerPath = scriptOptions.workerPath || scriptOptions.base; + scriptOptions.modePath = scriptOptions.modePath || scriptOptions.base; + scriptOptions.themePath = scriptOptions.themePath || scriptOptions.base; + delete scriptOptions.base; + + for (var key in scriptOptions) + if (typeof scriptOptions[key] !== "undefined") + config.set(key, scriptOptions[key]); + } + + function deHyphenate(str) { + return str.replace(/-(.)/g, function(m, m1) { + return m1.toUpperCase(); + }); + } +}); + +define("ace/range", ["require", "exports", "module"], function(require, exports, module) { + "use strict"; + var Range = /** @class */ (function() { + function Range(startRow, startColumn, endRow, endColumn) { + this.start = { + row: startRow, + column: startColumn + }; + this.end = { + row: endRow, + column: endColumn + }; + } + Range.prototype.isEqual = function(range) { + return this.start.row === range.start.row && + this.end.row === range.end.row && + this.start.column === range.start.column && + this.end.column === range.end.column; + }; + Range.prototype.toString = function() { + return ("Range: [" + this.start.row + "/" + this.start.column + + "] -> [" + this.end.row + "/" + this.end.column + "]"); + }; + Range.prototype.contains = function(row, column) { + return this.compare(row, column) == 0; + }; + Range.prototype.compareRange = function(range) { + var cmp, end = range.end, + start = range.start; + cmp = this.compare(end.row, end.column); + if (cmp == 1) { + cmp = this.compare(start.row, start.column); + if (cmp == 1) { + return 2; + } else if (cmp == 0) { + return 1; + } else { + return 0; + } + } else if (cmp == -1) { + return -2; + } else { + cmp = this.compare(start.row, start.column); + if (cmp == -1) { + return -1; + } else if (cmp == 1) { + return 42; + } else { + return 0; + } + } + }; + Range.prototype.comparePoint = function(p) { + return this.compare(p.row, p.column); + }; + Range.prototype.containsRange = function(range) { + return this.comparePoint(range.start) == 0 && this.comparePoint(range.end) == 0; + }; + Range.prototype.intersects = function(range) { + var cmp = this.compareRange(range); + return (cmp == -1 || cmp == 0 || cmp == 1); + }; + Range.prototype.isEnd = function(row, column) { + return this.end.row == row && this.end.column == column; + }; + Range.prototype.isStart = function(row, column) { + return this.start.row == row && this.start.column == column; + }; + Range.prototype.setStart = function(row, column) { + if (typeof row == "object") { + this.start.column = row.column; + this.start.row = row.row; + } else { + this.start.row = row; + this.start.column = column; + } + }; + Range.prototype.setEnd = function(row, column) { + if (typeof row == "object") { + this.end.column = row.column; + this.end.row = row.row; + } else { + this.end.row = row; + this.end.column = column; + } + }; + Range.prototype.inside = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isEnd(row, column) || this.isStart(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + Range.prototype.insideStart = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isEnd(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + Range.prototype.insideEnd = function(row, column) { + if (this.compare(row, column) == 0) { + if (this.isStart(row, column)) { + return false; + } else { + return true; + } + } + return false; + }; + Range.prototype.compare = function(row, column) { + if (!this.isMultiLine()) { + if (row === this.start.row) { + return column < this.start.column ? -1 : (column > this.end.column ? 1 : 0); + } + } + if (row < this.start.row) + return -1; + if (row > this.end.row) + return 1; + if (this.start.row === row) + return column >= this.start.column ? 0 : -1; + if (this.end.row === row) + return column <= this.end.column ? 0 : 1; + return 0; + }; + Range.prototype.compareStart = function(row, column) { + if (this.start.row == row && this.start.column == column) { + return -1; + } else { + return this.compare(row, column); + } + }; + Range.prototype.compareEnd = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } else { + return this.compare(row, column); + } + }; + Range.prototype.compareInside = function(row, column) { + if (this.end.row == row && this.end.column == column) { + return 1; + } else if (this.start.row == row && this.start.column == column) { + return -1; + } else { + return this.compare(row, column); + } + }; + Range.prototype.clipRows = function(firstRow, lastRow) { + if (this.end.row > lastRow) + var end = { + row: lastRow + 1, + column: 0 + }; + else if (this.end.row < firstRow) + var end = { + row: firstRow, + column: 0 + }; + if (this.start.row > lastRow) + var start = { + row: lastRow + 1, + column: 0 + }; + else if (this.start.row < firstRow) + var start = { + row: firstRow, + column: 0 + }; + return Range.fromPoints(start || this.start, end || this.end); + }; + Range.prototype.extend = function(row, column) { + var cmp = this.compare(row, column); + if (cmp == 0) + return this; + else if (cmp == -1) + var start = { + row: row, + column: column + }; + else + var end = { + row: row, + column: column + }; + return Range.fromPoints(start || this.start, end || this.end); + }; + Range.prototype.isEmpty = function() { + return (this.start.row === this.end.row && this.start.column === this.end.column); + }; + Range.prototype.isMultiLine = function() { + return (this.start.row !== this.end.row); + }; + Range.prototype.clone = function() { + return Range.fromPoints(this.start, this.end); + }; + Range.prototype.collapseRows = function() { + if (this.end.column == 0) + return new Range(this.start.row, 0, Math.max(this.start.row, this.end.row - 1), 0); + else + return new Range(this.start.row, 0, this.end.row, 0); + }; + Range.prototype.toScreenRange = function(session) { + var screenPosStart = session.documentToScreenPosition(this.start); + var screenPosEnd = session.documentToScreenPosition(this.end); + return new Range(screenPosStart.row, screenPosStart.column, screenPosEnd.row, screenPosEnd.column); + }; + Range.prototype.moveBy = function(row, column) { + this.start.row += row; + this.start.column += column; + this.end.row += row; + this.end.column += column; + }; + return Range; + }()); + Range.fromPoints = function(start, end) { + return new Range(start.row, start.column, end.row, end.column); + }; + Range.comparePoints = function(p1, p2) { + return p1.row - p2.row || p1.column - p2.column; + }; + exports.Range = Range; + +}); + +define("ace/lib/keys", ["require", "exports", "module", "ace/lib/oop"], function(require, exports, module) { + "use strict"; + var oop = require("./oop"); + var Keys = { + MODIFIER_KEYS: { + 16: 'Shift', + 17: 'Ctrl', + 18: 'Alt', + 224: 'Meta', + 91: 'MetaLeft', + 92: 'MetaRight', + 93: 'ContextMenu' + }, + KEY_MODS: { + "ctrl": 1, + "alt": 2, + "option": 2, + "shift": 4, + "super": 8, + "meta": 8, + "command": 8, + "cmd": 8, + "control": 1 + }, + FUNCTION_KEYS: { + 8: "Backspace", + 9: "Tab", + 13: "Return", + 19: "Pause", + 27: "Esc", + 32: "Space", + 33: "PageUp", + 34: "PageDown", + 35: "End", + 36: "Home", + 37: "Left", + 38: "Up", + 39: "Right", + 40: "Down", + 44: "Print", + 45: "Insert", + 46: "Delete", + '-13': "NumpadEnter", + 144: "Numlock", + 145: "Scrolllock" + }, + PRINTABLE_KEYS: { + 32: ' ', + 59: ';', + 61: '=', + 107: '+', + 109: '-', + 110: '.', + 186: ';', + 187: '=', + 188: ',', + 189: '-', + 190: '.', + 191: '/', + 192: '`', + 219: '[', + 220: '\\', + 221: ']', + 222: "'", + 111: '/', + 106: '*' + } + }; + var codeToKeyCode = { + Command: 224, + Backspace: 8, + Tab: 9, + Return: 13, + Enter: 13, + Pause: 19, + Escape: 27, + PageUp: 33, + PageDown: 34, + End: 35, + Home: 36, + Insert: 45, + Delete: 46, + ArrowLeft: 37, + ArrowUp: 38, + ArrowRight: 39, + ArrowDown: 40, + Backquote: 192, + Minus: 189, + Equal: 187, + BracketLeft: 219, + Backslash: 220, + BracketRight: 221, + Semicolon: 186, + Quote: 222, + Comma: 188, + Period: 190, + Slash: 191, + Space: 32, + NumpadAdd: 107, + NumpadDecimal: 110, + NumpadSubtract: 109, + NumpadDivide: 111, + NumpadMultiply: 106 + }; + for (var i = 0; i < 10; i++) { + codeToKeyCode["Digit" + i] = 48 + i; + codeToKeyCode["Numpad" + i] = 96 + i; + Keys.PRINTABLE_KEYS[48 + i] = "" + i; + Keys.FUNCTION_KEYS[96 + i] = "Numpad" + i; + } + for (var i = 65; i < 91; i++) { + var chr = String.fromCharCode(i + 32); + codeToKeyCode["Key" + chr.toUpperCase()] = i; + Keys.PRINTABLE_KEYS[i] = chr; + } + for (var i = 1; i < 13; i++) { + codeToKeyCode["F" + i] = 111 + i; + Keys.FUNCTION_KEYS[111 + i] = "F" + i; + } + var modifiers = { + Shift: 16, + Control: 17, + Alt: 18, + Meta: 224 + }; + for (var mod in modifiers) { + codeToKeyCode[mod] = codeToKeyCode[mod + "Left"] = codeToKeyCode[mod + "Right"] = modifiers[mod]; + } + exports.$codeToKeyCode = codeToKeyCode; + Keys.PRINTABLE_KEYS[173] = '-'; + for (var j in Keys.FUNCTION_KEYS) { + var name = Keys.FUNCTION_KEYS[j].toLowerCase(); + Keys[name] = parseInt(j, 10); + } + for (var j in Keys.PRINTABLE_KEYS) { + var name = Keys.PRINTABLE_KEYS[j].toLowerCase(); + Keys[name] = parseInt(j, 10); + } + oop.mixin(Keys, Keys.MODIFIER_KEYS); + oop.mixin(Keys, Keys.PRINTABLE_KEYS); + oop.mixin(Keys, Keys.FUNCTION_KEYS); + Keys.enter = Keys["return"]; + Keys.escape = Keys.esc; + Keys.del = Keys["delete"]; + (function() { + var mods = ["cmd", "ctrl", "alt", "shift"]; + for (var i = Math.pow(2, mods.length); i--;) { + Keys.KEY_MODS[i] = mods.filter(function(x) { + return i & Keys.KEY_MODS[x]; + }).join("-") + "-"; + } + })(); + Keys.KEY_MODS[0] = ""; + Keys.KEY_MODS[-1] = "input-"; + oop.mixin(exports, Keys); + exports.default = exports; + exports.keyCodeToString = function(keyCode) { + var keyString = Keys[keyCode]; + if (typeof keyString != "string") + keyString = String.fromCharCode(keyCode); + return keyString.toLowerCase(); + }; + +}); + +define("ace/lib/event", ["require", "exports", "module", "ace/lib/keys", "ace/lib/useragent"], function(require, exports, module) { + "use strict"; + var keys = require("./keys"); + var useragent = require("./useragent"); + var pressedKeys = null; + var ts = 0; + var activeListenerOptions; + + function detectListenerOptionsSupport() { + activeListenerOptions = false; + try { + document.createComment("").addEventListener("test", function() {}, { + get passive() { + activeListenerOptions = { + passive: false + }; + return true; + } + }); + } catch (e) {} + } + + function getListenerOptions() { + if (activeListenerOptions == undefined) + detectListenerOptionsSupport(); + return activeListenerOptions; + } + + function EventListener(elem, type, callback) { + this.elem = elem; + this.type = type; + this.callback = callback; + } + EventListener.prototype.destroy = function() { + removeListener(this.elem, this.type, this.callback); + this.elem = this.type = this.callback = undefined; + }; + var addListener = exports.addListener = function(elem, type, callback, /**@type{any?}*/ destroyer) { + elem.addEventListener(type, callback, getListenerOptions()); + if (destroyer) + destroyer.$toDestroy.push(new EventListener(elem, type, callback)); + }; + var removeListener = exports.removeListener = function(elem, type, callback) { + elem.removeEventListener(type, callback, getListenerOptions()); + }; + exports.stopEvent = function(e) { + exports.stopPropagation(e); + exports.preventDefault(e); + return false; + }; + exports.stopPropagation = function(e) { + if (e.stopPropagation) + e.stopPropagation(); + }; + exports.preventDefault = function(e) { + if (e.preventDefault) + e.preventDefault(); + }; + exports.getButton = function(e) { + if (e.type == "dblclick") + return 0; + if (e.type == "contextmenu" || (useragent.isMac && (e.ctrlKey && !e.altKey && !e.shiftKey))) + return 2; + return e.button; + }; + exports.capture = function(el, eventHandler, releaseCaptureHandler) { + var ownerDocument = el && el.ownerDocument || document; + + function onMouseUp(e) { + eventHandler && eventHandler(e); + releaseCaptureHandler && releaseCaptureHandler(e); + removeListener(ownerDocument, "mousemove", eventHandler); + removeListener(ownerDocument, "mouseup", onMouseUp); + removeListener(ownerDocument, "dragstart", onMouseUp); + } + addListener(ownerDocument, "mousemove", eventHandler); + addListener(ownerDocument, "mouseup", onMouseUp); + addListener(ownerDocument, "dragstart", onMouseUp); + return onMouseUp; + }; + exports.addMouseWheelListener = function(el, callback, destroyer) { + addListener(el, "wheel", function(e) { + var factor = 0.15; + var deltaX = e.deltaX || 0; + var deltaY = e.deltaY || 0; + switch (e.deltaMode) { + case e.DOM_DELTA_PIXEL: + e.wheelX = deltaX * factor; + e.wheelY = deltaY * factor; + break; + case e.DOM_DELTA_LINE: + var linePixels = 15; + e.wheelX = deltaX * linePixels; + e.wheelY = deltaY * linePixels; + break; + case e.DOM_DELTA_PAGE: + var pagePixels = 150; + e.wheelX = deltaX * pagePixels; + e.wheelY = deltaY * pagePixels; + break; + } + callback(e); + }, destroyer); + }; + exports.addMultiMouseDownListener = function(elements, timeouts, eventHandler, callbackName, destroyer) { + var clicks = 0; + var startX, startY, timer; + var eventNames = { + 2: "dblclick", + 3: "tripleclick", + 4: "quadclick" + }; + + function onMousedown(e) { + if (exports.getButton(e) !== 0) { + clicks = 0; + } else if (e.detail > 1) { + clicks++; + if (clicks > 4) + clicks = 1; + } else { + clicks = 1; + } + if (useragent.isIE) { + var isNewClick = Math.abs(e.clientX - startX) > 5 || Math.abs(e.clientY - startY) > 5; + if (!timer || isNewClick) + clicks = 1; + if (timer) + clearTimeout(timer); + timer = setTimeout(function() { + timer = null; + }, timeouts[clicks - 1] || 600); + if (clicks == 1) { + startX = e.clientX; + startY = e.clientY; + } + } + e._clicks = clicks; + eventHandler[callbackName]("mousedown", e); + if (clicks > 4) + clicks = 0; + else if (clicks > 1) + return eventHandler[callbackName](eventNames[clicks], e); + } + if (!Array.isArray(elements)) + elements = [elements]; + elements.forEach(function(el) { + addListener(el, "mousedown", onMousedown, destroyer); + }); + }; + + function getModifierHash(e) { + return 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0) | (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0); + } + exports.getModifierString = function(e) { + return keys.KEY_MODS[getModifierHash(e)]; + }; + + function normalizeCommandKeys(callback, e, keyCode) { + var hashId = getModifierHash(e); + if (!keyCode && e.code) { + keyCode = keys.$codeToKeyCode[e.code] || keyCode; + } + if (!useragent.isMac && pressedKeys) { + if (e.getModifierState && (e.getModifierState("OS") || e.getModifierState("Win"))) + hashId |= 8; + if (pressedKeys.altGr) { + if ((3 & hashId) != 3) + pressedKeys.altGr = 0; + else + return; + } + if (keyCode === 18 || keyCode === 17) { + var location = e.location; + if (keyCode === 17 && location === 1) { + if (pressedKeys[keyCode] == 1) + ts = e.timeStamp; + } else if (keyCode === 18 && hashId === 3 && location === 2) { + var dt = e.timeStamp - ts; + if (dt < 50) + pressedKeys.altGr = true; + } + } + } + if (keyCode in keys.MODIFIER_KEYS) { + keyCode = -1; + } + if (!hashId && keyCode === 13) { + if (e.location === 3) { + callback(e, hashId, -keyCode); + if (e.defaultPrevented) + return; + } + } + if (useragent.isChromeOS && hashId & 8) { + callback(e, hashId, keyCode); + if (e.defaultPrevented) + return; + else + hashId &= ~8; + } + if (!hashId && !(keyCode in keys.FUNCTION_KEYS) && !(keyCode in keys.PRINTABLE_KEYS)) { + return false; + } + return callback(e, hashId, keyCode); + } + exports.addCommandKeyListener = function(el, callback, destroyer) { + var lastDefaultPrevented = null; + addListener(el, "keydown", function(e) { + pressedKeys[e.keyCode] = (pressedKeys[e.keyCode] || 0) + 1; + var result = normalizeCommandKeys(callback, e, e.keyCode); + lastDefaultPrevented = e.defaultPrevented; + return result; + }, destroyer); + addListener(el, "keypress", function(e) { + if (lastDefaultPrevented && (e.ctrlKey || e.altKey || e.shiftKey || e.metaKey)) { + exports.stopEvent(e); + lastDefaultPrevented = null; + } + }, destroyer); + addListener(el, "keyup", function(e) { + pressedKeys[e.keyCode] = null; + }, destroyer); + if (!pressedKeys) { + resetPressedKeys(); + addListener(window, "focus", resetPressedKeys); + } + }; + + function resetPressedKeys() { + pressedKeys = Object.create(null); + } + if (typeof window == "object" && window.postMessage && !useragent.isOldIE) { + var postMessageId = 1; + exports.nextTick = function(callback, win) { + win = win || window; + var messageName = "zero-timeout-message-" + (postMessageId++); + var listener = function(e) { + if (e.data == messageName) { + exports.stopPropagation(e); + removeListener(win, "message", listener); + callback(); + } + }; + addListener(win, "message", listener); + win.postMessage(messageName, "*"); + }; + } + exports.$idleBlocked = false; + exports.onIdle = function(cb, timeout) { + return setTimeout(function handler() { + if (!exports.$idleBlocked) { + cb(); + } else { + setTimeout(handler, 100); + } + }, timeout); + }; + exports.$idleBlockId = null; + exports.blockIdle = function(delay) { + if (exports.$idleBlockId) + clearTimeout(exports.$idleBlockId); + exports.$idleBlocked = true; + exports.$idleBlockId = setTimeout(function() { + exports.$idleBlocked = false; + }, delay || 100); + }; + exports.nextFrame = typeof window == "object" && (window.requestAnimationFrame || + window["mozRequestAnimationFrame"] || + window["webkitRequestAnimationFrame"] || + window["msRequestAnimationFrame"] || + window["oRequestAnimationFrame"]); + if (exports.nextFrame) + exports.nextFrame = exports.nextFrame.bind(window); + else + exports.nextFrame = function(callback) { + setTimeout(callback, 17); + }; + +}); + +define("ace/clipboard", ["require", "exports", "module"], function(require, exports, module) { + "use strict"; + var $cancelT; + module.exports = { + lineMode: false, + pasteCancelled: function() { + if ($cancelT && $cancelT > Date.now() - 50) + return true; + return $cancelT = false; + }, + cancel: function() { + $cancelT = Date.now(); + } + }; + +}); + +define("ace/keyboard/textinput", ["require", "exports", "module", "ace/lib/event", "ace/config", "ace/lib/useragent", "ace/lib/dom", "ace/lib/lang", "ace/clipboard", "ace/lib/keys"], function(require, exports, module) { + "use strict"; + var event = require("../lib/event"); + var nls = require("../config").nls; + var useragent = require("../lib/useragent"); + var dom = require("../lib/dom"); + var lang = require("../lib/lang"); + var clipboard = require("../clipboard"); + var BROKEN_SETDATA = useragent.isChrome < 18; + var USE_IE_MIME_TYPE = useragent.isIE; + var HAS_FOCUS_ARGS = useragent.isChrome > 63; + var MAX_LINE_LENGTH = 400; + var KEYS = require("../lib/keys"); + var MODS = KEYS.KEY_MODS; + var isIOS = useragent.isIOS; + var valueResetRegex = isIOS ? /\s/ : /\n/; + var isMobile = useragent.isMobile; + var TextInput; + TextInput = function(parentNode, host) { + var text = dom.createElement("textarea"); + text.className = "ace_text-input"; + text.setAttribute("wrap", "off"); + text.setAttribute("autocorrect", "off"); + text.setAttribute("autocapitalize", "off"); + text.setAttribute("spellcheck", "false"); + text.style.opacity = "0"; + parentNode.insertBefore(text, parentNode.firstChild); + var copied = false; + var pasted = false; + var inComposition = false; + var sendingText = false; + var tempStyle = ''; + if (!isMobile) + text.style.fontSize = "1px"; + var commandMode = false; + var ignoreFocusEvents = false; + var lastValue = ""; + var lastSelectionStart = 0; + var lastSelectionEnd = 0; + var lastRestoreEnd = 0; + var rowStart = Number.MAX_SAFE_INTEGER; + var rowEnd = Number.MIN_SAFE_INTEGER; + var numberOfExtraLines = 0; + try { + var isFocused = document.activeElement === text; + } catch (e) {} + this.setNumberOfExtraLines = function(number) { + rowStart = Number.MAX_SAFE_INTEGER; + rowEnd = Number.MIN_SAFE_INTEGER; + if (number < 0) { + numberOfExtraLines = 0; + return; + } + numberOfExtraLines = number; + }; + this.setAriaOptions = function(options) { + if (options.activeDescendant) { + text.setAttribute("aria-haspopup", "true"); + text.setAttribute("aria-autocomplete", options.inline ? "both" : "list"); + text.setAttribute("aria-activedescendant", options.activeDescendant); + } else { + text.setAttribute("aria-haspopup", "false"); + text.setAttribute("aria-autocomplete", "both"); + text.removeAttribute("aria-activedescendant"); + } + if (options.role) { + text.setAttribute("role", options.role); + } + if (options.setLabel) { + text.setAttribute("aria-roledescription", nls("text-input.aria-roledescription", "editor")); + var arialLabel = ""; + if (host.$textInputAriaLabel) { + arialLabel += "".concat(host.$textInputAriaLabel, ", "); + } + if (host.session) { + var row = host.session.selection.cursor.row; + arialLabel += nls("text-input.aria-label", "Cursor at row $0", [row + 1]); + } + text.setAttribute("aria-label", arialLabel); + } + }; + this.setAriaOptions({ + role: "textbox" + }); + event.addListener(text, "blur", function(e) { + if (ignoreFocusEvents) + return; + host.onBlur(e); + isFocused = false; + }, host); + event.addListener(text, "focus", function(e) { + if (ignoreFocusEvents) + return; + isFocused = true; + if (useragent.isEdge) { + try { + if (!document.hasFocus()) + return; + } catch (e) {} + } + host.onFocus(e); + if (useragent.isEdge) + setTimeout(resetSelection); + else + resetSelection(); + }, host); + this.$focusScroll = false; + this.focus = function() { + this.setAriaOptions({ + setLabel: host.renderer.enableKeyboardAccessibility + }); + if (tempStyle || HAS_FOCUS_ARGS || this.$focusScroll == "browser") + return text.focus({ + preventScroll: true + }); + var top = text.style.top; + text.style.position = "fixed"; + text.style.top = "0px"; + try { + var isTransformed = text.getBoundingClientRect().top != 0; + } catch (e) { + return; + } + var ancestors = []; + if (isTransformed) { + var t = text.parentElement; + while (t && t.nodeType == 1) { + ancestors.push(t); + t.setAttribute("ace_nocontext", "true"); + if (!t.parentElement && t.getRootNode) + t = t.getRootNode()["host"]; + else + t = t.parentElement; + } + } + text.focus({ + preventScroll: true + }); + if (isTransformed) { + ancestors.forEach(function(p) { + p.removeAttribute("ace_nocontext"); + }); + } + setTimeout(function() { + text.style.position = ""; + if (text.style.top == "0px") + text.style.top = top; + }, 0); + }; + this.blur = function() { + text.blur(); + }; + this.isFocused = function() { + return isFocused; + }; + host.on("beforeEndOperation", function() { + var curOp = host.curOp; + var commandName = curOp && curOp.command && curOp.command.name; + if (commandName == "insertstring") + return; + var isUserAction = commandName && (curOp.docChanged || curOp.selectionChanged); + if (inComposition && isUserAction) { + lastValue = text.value = ""; + onCompositionEnd(); + } + resetSelection(); + }); + var positionToSelection = function(row, column) { + var selection = column; + for (var i = 1; i <= row - rowStart && i < 2 * numberOfExtraLines + 1; i++) { + selection += host.session.getLine(row - i).length + 1; + } + return selection; + }; + var resetSelection = isIOS ? + function(value) { + if (!isFocused || (copied && !value) || sendingText) + return; + if (!value) + value = ""; + var newValue = "\n ab" + value + "cde fg\n"; + if (newValue != text.value) + text.value = lastValue = newValue; + var selectionStart = 4; + var selectionEnd = 4 + (value.length || (host.selection.isEmpty() ? 0 : 1)); + if (lastSelectionStart != selectionStart || lastSelectionEnd != selectionEnd) { + text.setSelectionRange(selectionStart, selectionEnd); + } + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + } : + function() { + if (inComposition || sendingText) + return; + if (!isFocused && !afterContextMenu) + return; + inComposition = true; + var selectionStart = 0; + var selectionEnd = 0; + var line = ""; + if (host.session) { + var selection = host.selection; + var range = selection.getRange(); + var row = selection.cursor.row; + if (row === rowEnd + 1) { + rowStart = rowEnd + 1; + rowEnd = rowStart + 2 * numberOfExtraLines; + } else if (row === rowStart - 1) { + rowEnd = rowStart - 1; + rowStart = rowEnd - 2 * numberOfExtraLines; + } else if (row < rowStart - 1 || row > rowEnd + 1) { + rowStart = row > numberOfExtraLines ? row - numberOfExtraLines : 0; + rowEnd = row > numberOfExtraLines ? row + numberOfExtraLines : 2 * numberOfExtraLines; + } + var lines = []; + for (var i = rowStart; i <= rowEnd; i++) { + lines.push(host.session.getLine(i)); + } + line = lines.join('\n'); + selectionStart = positionToSelection(range.start.row, range.start.column); + selectionEnd = positionToSelection(range.end.row, range.end.column); + if (range.start.row < rowStart) { + var prevLine = host.session.getLine(rowStart - 1); + selectionStart = range.start.row < rowStart - 1 ? 0 : selectionStart; + selectionEnd += prevLine.length + 1; + line = prevLine + "\n" + line; + } else if (range.end.row > rowEnd) { + var nextLine = host.session.getLine(rowEnd + 1); + selectionEnd = range.end.row > rowEnd + 1 ? nextLine.length : range.end.column; + selectionEnd += line.length + 1; + line = line + "\n" + nextLine; + } else if (isMobile && row > 0) { + line = "\n" + line; + selectionEnd += 1; + selectionStart += 1; + } + if (line.length > MAX_LINE_LENGTH) { + if (selectionStart < MAX_LINE_LENGTH && selectionEnd < MAX_LINE_LENGTH) { + line = line.slice(0, MAX_LINE_LENGTH); + } else { + line = "\n"; + if (selectionStart == selectionEnd) { + selectionStart = selectionEnd = 0; + } else { + selectionStart = 0; + selectionEnd = 1; + } + } + } + var newValue = line + "\n\n"; + if (newValue != lastValue) { + text.value = lastValue = newValue; + lastSelectionStart = lastSelectionEnd = newValue.length; + } + } + if (afterContextMenu) { + lastSelectionStart = text.selectionStart; + lastSelectionEnd = text.selectionEnd; + } + if (lastSelectionEnd != selectionEnd || + lastSelectionStart != selectionStart || + text.selectionEnd != lastSelectionEnd // on ie edge selectionEnd changes silently after the initialization + ) { + try { + text.setSelectionRange(selectionStart, selectionEnd); + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + } catch (e) {} + } + inComposition = false; + }; + this.resetSelection = resetSelection; + if (isFocused) + host.onFocus(); + var isAllSelected = function(text) { + return text.selectionStart === 0 && text.selectionEnd >= lastValue.length && + text.value === lastValue && lastValue && + text.selectionEnd !== lastSelectionEnd; + }; + var onSelect = function(e) { + if (inComposition) + return; + if (copied) { + copied = false; + } else if (isAllSelected(text)) { + host.selectAll(); + resetSelection(); + } else if (isMobile && text.selectionStart != lastSelectionStart) { + resetSelection(); + } + }; + var inputHandler = null; + this.setInputHandler = function(cb) { + inputHandler = cb; + }; + this.getInputHandler = function() { + return inputHandler; + }; + var afterContextMenu = false; + var sendText = function(value, fromInput) { + if (afterContextMenu) + afterContextMenu = false; + if (pasted) { + resetSelection(); + if (value) + host.onPaste(value); + pasted = false; + return ""; + } else { + var selectionStart = text.selectionStart; + var selectionEnd = text.selectionEnd; + var extendLeft = lastSelectionStart; + var extendRight = lastValue.length - lastSelectionEnd; + var inserted = value; + var restoreStart = value.length - selectionStart; + var restoreEnd = value.length - selectionEnd; + var i = 0; + while (extendLeft > 0 && lastValue[i] == value[i]) { + i++; + extendLeft--; + } + inserted = inserted.slice(i); + i = 1; + while (extendRight > 0 && lastValue.length - i > lastSelectionStart - 1 && lastValue[lastValue.length - i] == value[value.length - i]) { + i++; + extendRight--; + } + restoreStart -= i - 1; + restoreEnd -= i - 1; + var endIndex = inserted.length - i + 1; + if (endIndex < 0) { + extendLeft = -endIndex; + endIndex = 0; + } + inserted = inserted.slice(0, endIndex); + if (!fromInput && !inserted && !restoreStart && !extendLeft && !extendRight && !restoreEnd) + return ""; + sendingText = true; + var shouldReset = false; + if (useragent.isAndroid && inserted == ". ") { + inserted = " "; + shouldReset = true; + } + if (inserted && !extendLeft && !extendRight && !restoreStart && !restoreEnd || commandMode) { + host.onTextInput(inserted); + } else { + host.onTextInput(inserted, { + extendLeft: extendLeft, + extendRight: extendRight, + restoreStart: restoreStart, + restoreEnd: restoreEnd + }); + } + sendingText = false; + lastValue = value; + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + lastRestoreEnd = restoreEnd; + return shouldReset ? "\n" : inserted; + } + }; + var onInput = function(e) { + if (inComposition) + return onCompositionUpdate(); + if (e && e.inputType) { + if (e.inputType == "historyUndo") + return host.execCommand("undo"); + if (e.inputType == "historyRedo") + return host.execCommand("redo"); + } + var data = text.value; + var inserted = sendText(data, true); + if (data.length > MAX_LINE_LENGTH + 100 || + valueResetRegex.test(inserted) || + isMobile && lastSelectionStart < 1 && lastSelectionStart == lastSelectionEnd) { + resetSelection(); + } + }; + var handleClipboardData = function(e, data, forceIEMime) { + var clipboardData = e.clipboardData || window["clipboardData"]; + if (!clipboardData || BROKEN_SETDATA) + return; + var mime = USE_IE_MIME_TYPE || forceIEMime ? "Text" : "text/plain"; + try { + if (data) { + return clipboardData.setData(mime, data) !== false; + } else { + return clipboardData.getData(mime); + } + } catch (e) { + if (!forceIEMime) + return handleClipboardData(e, data, true); + } + }; + var doCopy = function(e, isCut) { + var data = host.getCopyText(); + if (!data) + return event.preventDefault(e); + if (handleClipboardData(e, data)) { + if (isIOS) { + resetSelection(data); + copied = data; + setTimeout(function() { + copied = false; + }, 10); + } + isCut ? host.onCut() : host.onCopy(); + event.preventDefault(e); + } else { + copied = true; + text.value = data; + text.select(); + setTimeout(function() { + copied = false; + resetSelection(); + isCut ? host.onCut() : host.onCopy(); + }); + } + }; + var onCut = function(e) { + doCopy(e, true); + }; + var onCopy = function(e) { + doCopy(e, false); + }; + var onPaste = function(e) { + var data = handleClipboardData(e); + if (clipboard.pasteCancelled()) + return; + if (typeof data == "string") { + if (data) + host.onPaste(data, e); + if (useragent.isIE) + setTimeout(resetSelection); + event.preventDefault(e); + } else { + text.value = ""; + pasted = true; + } + }; + event.addCommandKeyListener(text, function(e, hashId, keyCode) { + if (inComposition) + return; + return host.onCommandKey(e, hashId, keyCode); + }, host); + event.addListener(text, "select", onSelect, host); + event.addListener(text, "input", onInput, host); + event.addListener(text, "cut", onCut, host); + event.addListener(text, "copy", onCopy, host); + event.addListener(text, "paste", onPaste, host); + if (!('oncut' in text) || !('oncopy' in text) || !('onpaste' in text)) { + event.addListener(parentNode, "keydown", function(e) { + if ((useragent.isMac && !e.metaKey) || !e.ctrlKey) + return; + switch (e.keyCode) { + case 67: + onCopy(e); + break; + case 86: + onPaste(e); + break; + case 88: + onCut(e); + break; + } + }, host); + } + var onCompositionStart = function(e) { + if (inComposition || !host.onCompositionStart || host.$readOnly) + return; + inComposition = {}; + if (commandMode) + return; + if (e.data) + inComposition.useTextareaForIME = false; + setTimeout(onCompositionUpdate, 0); + host._signal("compositionStart"); + host.on("mousedown", cancelComposition); + var range = host.getSelectionRange(); + range.end.row = range.start.row; + range.end.column = range.start.column; + inComposition.markerRange = range; + inComposition.selectionStart = lastSelectionStart; + host.onCompositionStart(inComposition); + if (inComposition.useTextareaForIME) { + lastValue = text.value = ""; + lastSelectionStart = 0; + lastSelectionEnd = 0; + } else { + if (text.msGetInputContext) + inComposition.context = text.msGetInputContext(); + if (text.getInputContext) + inComposition.context = text.getInputContext(); + } + }; + var onCompositionUpdate = function() { + if (!inComposition || !host.onCompositionUpdate || host.$readOnly) + return; + if (commandMode) + return cancelComposition(); + if (inComposition.useTextareaForIME) { + host.onCompositionUpdate(text.value); + } else { + var data = text.value; + sendText(data); + if (inComposition.markerRange) { + if (inComposition.context) { + inComposition.markerRange.start.column = inComposition.selectionStart = inComposition.context.compositionStartOffset; + } + inComposition.markerRange.end.column = inComposition.markerRange.start.column + + lastSelectionEnd - inComposition.selectionStart + lastRestoreEnd; + } + } + }; + var onCompositionEnd = function(e) { + if (!host.onCompositionEnd || host.$readOnly) + return; + inComposition = false; + host.onCompositionEnd(); + host.off("mousedown", cancelComposition); + if (e) + onInput(); + }; + + function cancelComposition() { + ignoreFocusEvents = true; + text.blur(); + text.focus(); + ignoreFocusEvents = false; + } + var syncComposition = lang.delayedCall(onCompositionUpdate, 50).schedule.bind(null, null); + + function onKeyup(e) { + if (e.keyCode == 27 && text.value.length < text.selectionStart) { + if (!inComposition) + lastValue = text.value; + lastSelectionStart = lastSelectionEnd = -1; + resetSelection(); + } + syncComposition(); + } + event.addListener(text, "compositionstart", onCompositionStart, host); + event.addListener(text, "compositionupdate", onCompositionUpdate, host); + event.addListener(text, "keyup", onKeyup, host); + event.addListener(text, "keydown", syncComposition, host); + event.addListener(text, "compositionend", onCompositionEnd, host); + this.getElement = function() { + return text; + }; + this.setCommandMode = function(value) { + commandMode = value; + text.readOnly = false; + }; + this.setReadOnly = function(readOnly) { + if (!commandMode) + text.readOnly = readOnly; + }; + this.setCopyWithEmptySelection = function(value) {}; + this.onContextMenu = function(e) { + afterContextMenu = true; + resetSelection(); + host._emit("nativecontextmenu", { + target: host, + domEvent: e + }); + this.moveToMouse(e, true); + }; + this.moveToMouse = function(e, bringToFront) { + if (!tempStyle) + tempStyle = text.style.cssText; + text.style.cssText = (bringToFront ? "z-index:100000;" : "") + + (useragent.isIE ? "opacity:0.1;" : "") + + "text-indent: -" + (lastSelectionStart + lastSelectionEnd) * host.renderer.characterWidth * 0.5 + "px;"; + var rect = host.container.getBoundingClientRect(); + var style = dom.computedStyle(host.container); + var top = rect.top + (parseInt(style.borderTopWidth) || 0); + var left = rect.left + (parseInt(rect.borderLeftWidth) || 0); + var maxTop = rect.bottom - top - text.clientHeight - 2; + var move = function(e) { + dom.translate(text, e.clientX - left - 2, Math.min(e.clientY - top - 2, maxTop)); + }; + move(e); + if (e.type != "mousedown") + return; + host.renderer.$isMousePressed = true; + clearTimeout(closeTimeout); + if (useragent.isWin) + event.capture(host.container, move, onContextMenuClose); + }; + this.onContextMenuClose = onContextMenuClose; + var closeTimeout; + + function onContextMenuClose() { + clearTimeout(closeTimeout); + closeTimeout = setTimeout(function() { + if (tempStyle) { + text.style.cssText = tempStyle; + tempStyle = ''; + } + host.renderer.$isMousePressed = false; + if (host.renderer.$keepTextAreaAtCursor) + host.renderer.$moveTextAreaToCursor(); + }, 0); + } + var onContextMenu = function(e) { + host.textInput.onContextMenu(e); + onContextMenuClose(); + }; + event.addListener(text, "mouseup", onContextMenu, host); + event.addListener(text, "mousedown", function(e) { + e.preventDefault(); + onContextMenuClose(); + }, host); + event.addListener(host.renderer.scroller, "contextmenu", onContextMenu, host); + event.addListener(text, "contextmenu", onContextMenu, host); + if (isIOS) + addIosSelectionHandler(parentNode, host, text); + + function addIosSelectionHandler(parentNode, host, text) { + var typingResetTimeout = null; + var typing = false; + text.addEventListener("keydown", function(e) { + if (typingResetTimeout) + clearTimeout(typingResetTimeout); + typing = true; + }, true); + text.addEventListener("keyup", function(e) { + typingResetTimeout = setTimeout(function() { + typing = false; + }, 100); + }, true); + var detectArrowKeys = function(e) { + if (document.activeElement !== text) + return; + if (typing || inComposition || host.$mouseHandler.isMousePressed) + return; + if (copied) { + return; + } + var selectionStart = text.selectionStart; + var selectionEnd = text.selectionEnd; + var key = null; + var modifier = 0; + if (selectionStart == 0) { + key = KEYS.up; + } else if (selectionStart == 1) { + key = KEYS.home; + } else if (selectionEnd > lastSelectionEnd && lastValue[selectionEnd] == "\n") { + key = KEYS.end; + } else if (selectionStart < lastSelectionStart && lastValue[selectionStart - 1] == " ") { + key = KEYS.left; + modifier = MODS.option; + } else if (selectionStart < lastSelectionStart || + (selectionStart == lastSelectionStart && + lastSelectionEnd != lastSelectionStart && + selectionStart == selectionEnd)) { + key = KEYS.left; + } else if (selectionEnd > lastSelectionEnd && lastValue.slice(0, selectionEnd).split("\n").length > 2) { + key = KEYS.down; + } else if (selectionEnd > lastSelectionEnd && lastValue[selectionEnd - 1] == " ") { + key = KEYS.right; + modifier = MODS.option; + } else if (selectionEnd > lastSelectionEnd || + (selectionEnd == lastSelectionEnd && + lastSelectionEnd != lastSelectionStart && + selectionStart == selectionEnd)) { + key = KEYS.right; + } + if (selectionStart !== selectionEnd) + modifier |= MODS.shift; + if (key) { + var result = host.onCommandKey({}, modifier, key); + if (!result && host.commands) { + key = KEYS.keyCodeToString(key); + var command = host.commands.findKeyCommand(modifier, key); + if (command) + host.execCommand(command); + } + lastSelectionStart = selectionStart; + lastSelectionEnd = selectionEnd; + resetSelection(""); + } + }; + document.addEventListener("selectionchange", detectArrowKeys); + host.on("destroy", function() { + document.removeEventListener("selectionchange", detectArrowKeys); + }); + } + this.destroy = function() { + if (text.parentElement) + text.parentElement.removeChild(text); + }; + }; + exports.TextInput = TextInput; + exports.$setUserAgentForTests = function(_isMobile, _isIOS) { + isMobile = _isMobile; + isIOS = _isIOS; + }; + +}); + +define("ace/mouse/default_handlers", ["require", "exports", "module", "ace/lib/useragent"], function(require, exports, module) { + "use strict"; + var useragent = require("../lib/useragent"); + var DRAG_OFFSET = 0; // pixels + var SCROLL_COOLDOWN_T = 550; // milliseconds + var DefaultHandlers = /** @class */ (function() { + function DefaultHandlers(mouseHandler) { + mouseHandler.$clickSelection = null; + var editor = mouseHandler.editor; + editor.setDefaultHandler("mousedown", this.onMouseDown.bind(mouseHandler)); + editor.setDefaultHandler("dblclick", this.onDoubleClick.bind(mouseHandler)); + editor.setDefaultHandler("tripleclick", this.onTripleClick.bind(mouseHandler)); + editor.setDefaultHandler("quadclick", this.onQuadClick.bind(mouseHandler)); + editor.setDefaultHandler("mousewheel", this.onMouseWheel.bind(mouseHandler)); + var exports = ["select", "startSelect", "selectEnd", "selectAllEnd", "selectByWordsEnd", + "selectByLinesEnd", "dragWait", "dragWaitEnd", "focusWait" + ]; + exports.forEach(function(x) { + mouseHandler[x] = this[x]; + }, this); + mouseHandler["selectByLines"] = this.extendSelectionBy.bind(mouseHandler, "getLineRange"); + mouseHandler["selectByWords"] = this.extendSelectionBy.bind(mouseHandler, "getWordRange"); + } + DefaultHandlers.prototype.onMouseDown = function(ev) { + var inSelection = ev.inSelection(); + var pos = ev.getDocumentPosition(); + this.mousedownEvent = ev; + var editor = this.editor; + var button = ev.getButton(); + if (button !== 0) { + var selectionRange = editor.getSelectionRange(); + var selectionEmpty = selectionRange.isEmpty(); + if (selectionEmpty || button == 1) + editor.selection.moveToPosition(pos); + if (button == 2) { + editor.textInput.onContextMenu(ev.domEvent); + if (!useragent.isMozilla) + ev.preventDefault(); + } + return; + } + this.mousedownEvent.time = Date.now(); + if (inSelection && !editor.isFocused()) { + editor.focus(); + if (this.$focusTimeout && !this.$clickSelection && !editor.inMultiSelectMode) { + this.setState("focusWait"); + this.captureMouse(ev); + return; + } + } + this.captureMouse(ev); + this.startSelect(pos, ev.domEvent._clicks > 1); + return ev.preventDefault(); + }; + DefaultHandlers.prototype.startSelect = function(pos, waitForClickSelection) { + pos = pos || this.editor.renderer.screenToTextCoordinates(this.x, this.y); + var editor = this.editor; + if (!this.mousedownEvent) + return; + if (this.mousedownEvent.getShiftKey()) + editor.selection.selectToPosition(pos); + else if (!waitForClickSelection) + editor.selection.moveToPosition(pos); + if (!waitForClickSelection) + this.select(); + editor.setStyle("ace_selecting"); + this.setState("select"); + }; + DefaultHandlers.prototype.select = function() { + var anchor, editor = this.editor; + var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); + if (this.$clickSelection) { + var cmp = this.$clickSelection.comparePoint(cursor); + if (cmp == -1) { + anchor = this.$clickSelection.end; + } else if (cmp == 1) { + anchor = this.$clickSelection.start; + } else { + var orientedRange = calcRangeOrientation(this.$clickSelection, cursor); + cursor = orientedRange.cursor; + anchor = orientedRange.anchor; + } + editor.selection.setSelectionAnchor(anchor.row, anchor.column); + } + editor.selection.selectToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + }; + DefaultHandlers.prototype.extendSelectionBy = function(unitName) { + var anchor, editor = this.editor; + var cursor = editor.renderer.screenToTextCoordinates(this.x, this.y); + var range = editor.selection[unitName](cursor.row, cursor.column); + if (this.$clickSelection) { + var cmpStart = this.$clickSelection.comparePoint(range.start); + var cmpEnd = this.$clickSelection.comparePoint(range.end); + if (cmpStart == -1 && cmpEnd <= 0) { + anchor = this.$clickSelection.end; + if (range.end.row != cursor.row || range.end.column != cursor.column) + cursor = range.start; + } else if (cmpEnd == 1 && cmpStart >= 0) { + anchor = this.$clickSelection.start; + if (range.start.row != cursor.row || range.start.column != cursor.column) + cursor = range.end; + } else if (cmpStart == -1 && cmpEnd == 1) { + cursor = range.end; + anchor = range.start; + } else { + var orientedRange = calcRangeOrientation(this.$clickSelection, cursor); + cursor = orientedRange.cursor; + anchor = orientedRange.anchor; + } + editor.selection.setSelectionAnchor(anchor.row, anchor.column); + } + editor.selection.selectToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + }; + DefaultHandlers.prototype.selectByLinesEnd = function() { + this.$clickSelection = null; + this.editor.unsetStyle("ace_selecting"); + }; + DefaultHandlers.prototype.focusWait = function() { + var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); + var time = Date.now(); + if (distance > DRAG_OFFSET || time - this.mousedownEvent.time > this.$focusTimeout) + this.startSelect(this.mousedownEvent.getDocumentPosition()); + }; + DefaultHandlers.prototype.onDoubleClick = function(ev) { + var pos = ev.getDocumentPosition(); + var editor = this.editor; + var session = editor.session; + var range = session.getBracketRange(pos); + if (range) { + if (range.isEmpty()) { + range.start.column--; + range.end.column++; + } + this.setState("select"); + } else { + range = editor.selection.getWordRange(pos.row, pos.column); + this.setState("selectByWords"); + } + this.$clickSelection = range; + this.select(); + }; + DefaultHandlers.prototype.onTripleClick = function(ev) { + var pos = ev.getDocumentPosition(); + var editor = this.editor; + this.setState("selectByLines"); + var range = editor.getSelectionRange(); + if (range.isMultiLine() && range.contains(pos.row, pos.column)) { + this.$clickSelection = editor.selection.getLineRange(range.start.row); + this.$clickSelection.end = editor.selection.getLineRange(range.end.row).end; + } else { + this.$clickSelection = editor.selection.getLineRange(pos.row); + } + this.select(); + }; + DefaultHandlers.prototype.onQuadClick = function(ev) { + var editor = this.editor; + editor.selectAll(); + this.$clickSelection = editor.getSelectionRange(); + this.setState("selectAll"); + }; + DefaultHandlers.prototype.onMouseWheel = function(ev) { + if (ev.getAccelKey()) + return; + if (ev.getShiftKey() && ev.wheelY && !ev.wheelX) { + ev.wheelX = ev.wheelY; + ev.wheelY = 0; + } + var editor = this.editor; + if (!this.$lastScroll) + this.$lastScroll = { + t: 0, + vx: 0, + vy: 0, + allowed: 0 + }; + var prevScroll = this.$lastScroll; + var t = ev.domEvent.timeStamp; + var dt = t - prevScroll.t; + var vx = dt ? ev.wheelX / dt : prevScroll.vx; + var vy = dt ? ev.wheelY / dt : prevScroll.vy; + if (dt < SCROLL_COOLDOWN_T) { + vx = (vx + prevScroll.vx) / 2; + vy = (vy + prevScroll.vy) / 2; + } + var direction = Math.abs(vx / vy); + var canScroll = false; + if (direction >= 1 && editor.renderer.isScrollableBy(ev.wheelX * ev.speed, 0)) + canScroll = true; + if (direction <= 1 && editor.renderer.isScrollableBy(0, ev.wheelY * ev.speed)) + canScroll = true; + if (canScroll) { + prevScroll.allowed = t; + } else if (t - prevScroll.allowed < SCROLL_COOLDOWN_T) { + var isSlower = Math.abs(vx) <= 1.5 * Math.abs(prevScroll.vx) && + Math.abs(vy) <= 1.5 * Math.abs(prevScroll.vy); + if (isSlower) { + canScroll = true; + prevScroll.allowed = t; + } else { + prevScroll.allowed = 0; + } + } + prevScroll.t = t; + prevScroll.vx = vx; + prevScroll.vy = vy; + if (canScroll) { + editor.renderer.scrollBy(ev.wheelX * ev.speed, ev.wheelY * ev.speed); + return ev.stop(); + } + }; + return DefaultHandlers; + }()); + DefaultHandlers.prototype.selectEnd = DefaultHandlers.prototype.selectByLinesEnd; + DefaultHandlers.prototype.selectAllEnd = DefaultHandlers.prototype.selectByLinesEnd; + DefaultHandlers.prototype.selectByWordsEnd = DefaultHandlers.prototype.selectByLinesEnd; + exports.DefaultHandlers = DefaultHandlers; + + function calcDistance(ax, ay, bx, by) { + return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); + } + + function calcRangeOrientation(range, cursor) { + if (range.start.row == range.end.row) + var cmp = 2 * cursor.column - range.start.column - range.end.column; + else if (range.start.row == range.end.row - 1 && !range.start.column && !range.end.column) + var cmp = cursor.column - 4; + else + var cmp = 2 * cursor.row - range.start.row - range.end.row; + if (cmp < 0) + return { + cursor: range.start, + anchor: range.end + }; + else + return { + cursor: range.end, + anchor: range.start + }; + } + +}); + +define("ace/lib/scroll", ["require", "exports", "module"], function(require, exports, module) { + exports.preventParentScroll = function preventParentScroll(event) { + event.stopPropagation(); + var target = event.currentTarget; + var contentOverflows = target.scrollHeight > target.clientHeight; + if (!contentOverflows) { + event.preventDefault(); + } + }; + +}); + +define("ace/tooltip", ["require", "exports", "module", "ace/lib/dom", "ace/lib/event", "ace/range", "ace/lib/scroll"], function(require, exports, module) { + "use strict"; + var __extends = (this && this.__extends) || (function() { + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ + __proto__: [] + } + instanceof Array && function(d, b) { + d.__proto__ = b; + }) || + function(d, b) { + for (var p in b) + if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); + }; + return function(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, + m = s && o[s], + i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function() { + if (o && i >= o.length) o = void 0; + return { + value: o && o[i++], + done: !o + }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); + }; + var dom = require("./lib/dom"); + var event = require("./lib/event"); + var Range = require("./range").Range; + var preventParentScroll = require("./lib/scroll").preventParentScroll; + var CLASSNAME = "ace_tooltip"; + var Tooltip = /** @class */ (function() { + function Tooltip(parentNode) { + this.isOpen = false; + this.$element = null; + this.$parentNode = parentNode; + } + Tooltip.prototype.$init = function() { + this.$element = dom.createElement("div"); + this.$element.className = CLASSNAME; + this.$element.style.display = "none"; + this.$parentNode.appendChild(this.$element); + return this.$element; + }; + Tooltip.prototype.getElement = function() { + return this.$element || this.$init(); + }; + Tooltip.prototype.setText = function(text) { + this.getElement().textContent = text; + }; + Tooltip.prototype.setHtml = function(html) { + this.getElement().innerHTML = html; + }; + Tooltip.prototype.setPosition = function(x, y) { + this.getElement().style.left = x + "px"; + this.getElement().style.top = y + "px"; + }; + Tooltip.prototype.setClassName = function(className) { + dom.addCssClass(this.getElement(), className); + }; + Tooltip.prototype.setTheme = function(theme) { + this.$element.className = CLASSNAME + " " + + (theme.isDark ? "ace_dark " : "") + (theme.cssClass || ""); + }; + Tooltip.prototype.show = function(text, x, y) { + if (text != null) + this.setText(text); + if (x != null && y != null) + this.setPosition(x, y); + if (!this.isOpen) { + this.getElement().style.display = "block"; + this.isOpen = true; + } + }; + Tooltip.prototype.hide = function(e) { + if (this.isOpen) { + this.getElement().style.display = "none"; + this.getElement().className = CLASSNAME; + this.isOpen = false; + } + }; + Tooltip.prototype.getHeight = function() { + return this.getElement().offsetHeight; + }; + Tooltip.prototype.getWidth = function() { + return this.getElement().offsetWidth; + }; + Tooltip.prototype.destroy = function() { + this.isOpen = false; + if (this.$element && this.$element.parentNode) { + this.$element.parentNode.removeChild(this.$element); + } + }; + return Tooltip; + }()); + var PopupManager = /** @class */ (function() { + function PopupManager() { + this.popups = []; + } + PopupManager.prototype.addPopup = function(popup) { + this.popups.push(popup); + this.updatePopups(); + }; + PopupManager.prototype.removePopup = function(popup) { + var index = this.popups.indexOf(popup); + if (index !== -1) { + this.popups.splice(index, 1); + this.updatePopups(); + } + }; + PopupManager.prototype.updatePopups = function() { + var e_1, _a, e_2, _b; + this.popups.sort(function(a, b) { + return b.priority - a.priority; + }); + var visiblepopups = []; + try { + for (var _c = __values(this.popups), _d = _c.next(); !_d.done; _d = _c.next()) { + var popup = _d.value; + var shouldDisplay = true; + try { + for (var visiblepopups_1 = (e_2 = void 0, __values(visiblepopups)), visiblepopups_1_1 = visiblepopups_1.next(); !visiblepopups_1_1.done; visiblepopups_1_1 = visiblepopups_1.next()) { + var visiblePopup = visiblepopups_1_1.value; + if (this.doPopupsOverlap(visiblePopup, popup)) { + shouldDisplay = false; + break; + } + } + } catch (e_2_1) { + e_2 = { + error: e_2_1 + }; + } finally { + try { + if (visiblepopups_1_1 && !visiblepopups_1_1.done && (_b = visiblepopups_1.return)) _b.call(visiblepopups_1); + } finally { + if (e_2) throw e_2.error; + } + } + if (shouldDisplay) { + visiblepopups.push(popup); + } else { + popup.hide(); + } + } + } catch (e_1_1) { + e_1 = { + error: e_1_1 + }; + } finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } finally { + if (e_1) throw e_1.error; + } + } + }; + PopupManager.prototype.doPopupsOverlap = function(popupA, popupB) { + var rectA = popupA.getElement().getBoundingClientRect(); + var rectB = popupB.getElement().getBoundingClientRect(); + return (rectA.left < rectB.right && rectA.right > rectB.left && rectA.top < rectB.bottom && rectA.bottom > + rectB.top); + }; + return PopupManager; + }()); + var popupManager = new PopupManager(); + exports.popupManager = popupManager; + exports.Tooltip = Tooltip; + var HoverTooltip = /** @class */ (function(_super) { + __extends(HoverTooltip, _super); + + function HoverTooltip(parentNode) { + if (parentNode === void 0) { + parentNode = document.body; + } + var _this = _super.call(this, parentNode) || this; + _this.timeout = undefined; + _this.lastT = 0; + _this.idleTime = 350; + _this.lastEvent = undefined; + _this.onMouseOut = _this.onMouseOut.bind(_this); + _this.onMouseMove = _this.onMouseMove.bind(_this); + _this.waitForHover = _this.waitForHover.bind(_this); + _this.hide = _this.hide.bind(_this); + var el = _this.getElement(); + el.style.whiteSpace = "pre-wrap"; + el.style.pointerEvents = "auto"; + el.addEventListener("mouseout", _this.onMouseOut); + el.tabIndex = -1; + el.addEventListener("blur", function() { + if (!el.contains(document.activeElement)) + this.hide(); + }.bind(_this)); + el.addEventListener("wheel", preventParentScroll); + return _this; + } + HoverTooltip.prototype.addToEditor = function(editor) { + editor.on("mousemove", this.onMouseMove); + editor.on("mousedown", this.hide); + editor.renderer.getMouseEventTarget().addEventListener("mouseout", this.onMouseOut, true); + }; + HoverTooltip.prototype.removeFromEditor = function(editor) { + editor.off("mousemove", this.onMouseMove); + editor.off("mousedown", this.hide); + editor.renderer.getMouseEventTarget().removeEventListener("mouseout", this.onMouseOut, true); + if (this.timeout) { + clearTimeout(this.timeout); + this.timeout = null; + } + }; + HoverTooltip.prototype.onMouseMove = function(e, editor) { + this.lastEvent = e; + this.lastT = Date.now(); + var isMousePressed = editor.$mouseHandler.isMousePressed; + if (this.isOpen) { + var pos = this.lastEvent && this.lastEvent.getDocumentPosition(); + if (!this.range || + !this.range.contains(pos.row, pos.column) || + isMousePressed || + this.isOutsideOfText(this.lastEvent)) { + this.hide(); + } + } + if (this.timeout || isMousePressed) + return; + this.lastEvent = e; + this.timeout = setTimeout(this.waitForHover, this.idleTime); + }; + HoverTooltip.prototype.waitForHover = function() { + if (this.timeout) + clearTimeout(this.timeout); + var dt = Date.now() - this.lastT; + if (this.idleTime - dt > 10) { + this.timeout = setTimeout(this.waitForHover, this.idleTime - dt); + return; + } + this.timeout = null; + if (this.lastEvent && !this.isOutsideOfText(this.lastEvent)) { + this.$gatherData(this.lastEvent, this.lastEvent.editor); + } + }; + HoverTooltip.prototype.isOutsideOfText = function(e) { + var editor = e.editor; + var docPos = e.getDocumentPosition(); + var line = editor.session.getLine(docPos.row); + if (docPos.column == line.length) { + var screenPos = editor.renderer.pixelToScreenCoordinates(e.clientX, e.clientY); + var clippedPos = editor.session.documentToScreenPosition(docPos.row, docPos.column); + if (clippedPos.column != screenPos.column || + clippedPos.row != screenPos.row) { + return true; + } + } + return false; + }; + HoverTooltip.prototype.setDataProvider = function(value) { + this.$gatherData = value; + }; + HoverTooltip.prototype.showForRange = function(editor, range, domNode, startingEvent) { + var MARGIN = 10; + if (startingEvent && startingEvent != this.lastEvent) + return; + if (this.isOpen && document.activeElement == this.getElement()) + return; + var renderer = editor.renderer; + if (!this.isOpen) { + popupManager.addPopup(this); + this.$registerCloseEvents(); + this.setTheme(renderer.theme); + } + this.isOpen = true; + this.addMarker(range, editor.session); + this.range = Range.fromPoints(range.start, range.end); + var position = renderer.textToScreenCoordinates(range.start.row, range.start.column); + var rect = renderer.scroller.getBoundingClientRect(); + if (position.pageX < rect.left) + position.pageX = rect.left; + var element = this.getElement(); + element.innerHTML = ""; + element.appendChild(domNode); + element.style.maxHeight = ""; + element.style.display = "block"; + var labelHeight = element.clientHeight; + var labelWidth = element.clientWidth; + var spaceBelow = window.innerHeight - position.pageY - renderer.lineHeight; + var isAbove = true; + if (position.pageY - labelHeight < 0 && position.pageY < spaceBelow) { + isAbove = false; + } + element.style.maxHeight = (isAbove ? position.pageY : spaceBelow) - MARGIN + "px"; + element.style.top = isAbove ? "" : position.pageY + renderer.lineHeight + "px"; + element.style.bottom = isAbove ? window.innerHeight - position.pageY + "px" : ""; + element.style.left = Math.min(position.pageX, window.innerWidth - labelWidth - MARGIN) + "px"; + }; + HoverTooltip.prototype.addMarker = function(range, session) { + if (this.marker) { + this.$markerSession.removeMarker(this.marker); + } + this.$markerSession = session; + this.marker = session && session.addMarker(range, "ace_highlight-marker", "text"); + }; + HoverTooltip.prototype.hide = function(e) { + if (!e && document.activeElement == this.getElement()) + return; + if (e && e.target && (e.type != "keydown" || e.ctrlKey || e.metaKey) && this.$element.contains(e.target)) + return; + this.lastEvent = null; + if (this.timeout) + clearTimeout(this.timeout); + this.timeout = null; + this.addMarker(null); + if (this.isOpen) { + this.$removeCloseEvents(); + this.getElement().style.display = "none"; + this.isOpen = false; + popupManager.removePopup(this); + } + }; + HoverTooltip.prototype.$registerCloseEvents = function() { + window.addEventListener("keydown", this.hide, true); + window.addEventListener("wheel", this.hide, true); + window.addEventListener("mousedown", this.hide, true); + }; + HoverTooltip.prototype.$removeCloseEvents = function() { + window.removeEventListener("keydown", this.hide, true); + window.removeEventListener("wheel", this.hide, true); + window.removeEventListener("mousedown", this.hide, true); + }; + HoverTooltip.prototype.onMouseOut = function(e) { + if (this.timeout) { + clearTimeout(this.timeout); + this.timeout = null; + } + this.lastEvent = null; + if (!this.isOpen) + return; + if (!e.relatedTarget || this.getElement().contains(e.relatedTarget)) + return; + if (e && e.currentTarget.contains(e.relatedTarget)) + return; + if (!e.relatedTarget.classList.contains("ace_content")) + this.hide(); + }; + return HoverTooltip; + }(Tooltip)); + exports.HoverTooltip = HoverTooltip; + +}); + +define("ace/mouse/default_gutter_handler", ["require", "exports", "module", "ace/lib/dom", "ace/lib/event", "ace/tooltip", "ace/config", "ace/lib/lang"], function(require, exports, module) { + "use strict"; + var __extends = (this && this.__extends) || (function() { + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ + __proto__: [] + } + instanceof Array && function(d, b) { + d.__proto__ = b; + }) || + function(d, b) { + for (var p in b) + if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); + }; + return function(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, + m = s && o[s], + i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function() { + if (o && i >= o.length) o = void 0; + return { + value: o && o[i++], + done: !o + }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); + }; + var dom = require("../lib/dom"); + var event = require("../lib/event"); + var Tooltip = require("../tooltip").Tooltip; + var nls = require("../config").nls; + var lang = require("../lib/lang"); + + function GutterHandler(mouseHandler) { + var editor = mouseHandler.editor; + var gutter = editor.renderer.$gutterLayer; + var tooltip = new GutterTooltip(editor); + mouseHandler.editor.setDefaultHandler("guttermousedown", function(e) { + if (!editor.isFocused() || e.getButton() != 0) + return; + var gutterRegion = gutter.getRegion(e); + if (gutterRegion == "foldWidgets") + return; + var row = e.getDocumentPosition().row; + var selection = editor.session.selection; + if (e.getShiftKey()) + selection.selectTo(row, 0); + else { + if (e.domEvent.detail == 2) { + editor.selectAll(); + return e.preventDefault(); + } + mouseHandler.$clickSelection = editor.selection.getLineRange(row); + } + mouseHandler.setState("selectByLines"); + mouseHandler.captureMouse(e); + return e.preventDefault(); + }); + var tooltipTimeout, mouseEvent; + + function showTooltip() { + var row = mouseEvent.getDocumentPosition().row; + var maxRow = editor.session.getLength(); + if (row == maxRow) { + var screenRow = editor.renderer.pixelToScreenCoordinates(0, mouseEvent.y).row; + var pos = mouseEvent.$pos; + if (screenRow > editor.session.documentToScreenRow(pos.row, pos.column)) + return hideTooltip(); + } + tooltip.showTooltip(row); + if (!tooltip.isOpen) + return; + editor.on("mousewheel", hideTooltip); + if (mouseHandler.$tooltipFollowsMouse) { + moveTooltip(mouseEvent); + } else { + var gutterRow = mouseEvent.getGutterRow(); + var gutterCell = gutter.$lines.get(gutterRow); + if (gutterCell) { + var gutterElement = gutterCell.element.querySelector(".ace_gutter_annotation"); + var rect = gutterElement.getBoundingClientRect(); + var style = tooltip.getElement().style; + style.left = rect.right + "px"; + style.top = rect.bottom + "px"; + } else { + moveTooltip(mouseEvent); + } + } + } + + function hideTooltip() { + if (tooltipTimeout) + tooltipTimeout = clearTimeout(tooltipTimeout); + if (tooltip.isOpen) { + tooltip.hideTooltip(); + editor.off("mousewheel", hideTooltip); + } + } + + function moveTooltip(e) { + tooltip.setPosition(e.x, e.y); + } + mouseHandler.editor.setDefaultHandler("guttermousemove", function(e) { + var target = e.domEvent.target || e.domEvent.srcElement; + if (dom.hasCssClass(target, "ace_fold-widget")) + return hideTooltip(); + if (tooltip.isOpen && mouseHandler.$tooltipFollowsMouse) + moveTooltip(e); + mouseEvent = e; + if (tooltipTimeout) + return; + tooltipTimeout = setTimeout(function() { + tooltipTimeout = null; + if (mouseEvent && !mouseHandler.isMousePressed) + showTooltip(); + else + hideTooltip(); + }, 50); + }); + event.addListener(editor.renderer.$gutter, "mouseout", function(e) { + mouseEvent = null; + if (!tooltip.isOpen || tooltipTimeout) + return; + tooltipTimeout = setTimeout(function() { + tooltipTimeout = null; + hideTooltip(); + }, 50); + }, editor); + editor.on("changeSession", hideTooltip); + editor.on("input", hideTooltip); + } + exports.GutterHandler = GutterHandler; + var GutterTooltip = /** @class */ (function(_super) { + __extends(GutterTooltip, _super); + + function GutterTooltip(editor) { + var _this = _super.call(this, editor.container) || this; + _this.editor = editor; + return _this; + } + GutterTooltip.prototype.setPosition = function(x, y) { + var windowWidth = window.innerWidth || document.documentElement.clientWidth; + var windowHeight = window.innerHeight || document.documentElement.clientHeight; + var width = this.getWidth(); + var height = this.getHeight(); + x += 15; + y += 15; + if (x + width > windowWidth) { + x -= (x + width) - windowWidth; + } + if (y + height > windowHeight) { + y -= 20 + height; + } + Tooltip.prototype.setPosition.call(this, x, y); + }; + Object.defineProperty(GutterTooltip, "annotationLabels", { + get: function() { + return { + error: { + singular: nls("gutter-tooltip.aria-label.error.singular", "error"), + plural: nls("gutter-tooltip.aria-label.error.plural", "errors") + }, + warning: { + singular: nls("gutter-tooltip.aria-label.warning.singular", "warning"), + plural: nls("gutter-tooltip.aria-label.warning.plural", "warnings") + }, + info: { + singular: nls("gutter-tooltip.aria-label.info.singular", "information message"), + plural: nls("gutter-tooltip.aria-label.info.plural", "information messages") + } + }; + }, + enumerable: false, + configurable: true + }); + GutterTooltip.prototype.showTooltip = function(row) { + var _a; + var gutter = this.editor.renderer.$gutterLayer; + var annotationsInRow = gutter.$annotations[row]; + var annotation; + if (annotationsInRow) + annotation = { + displayText: Array.from(annotationsInRow.displayText), + type: Array.from(annotationsInRow.type) + }; + else + annotation = { + displayText: [], + type: [] + }; + var fold = gutter.session.getFoldLine(row); + if (fold && gutter.$showFoldedAnnotations) { + var annotationsInFold = { + error: [], + warning: [], + info: [] + }; + var mostSevereAnnotationInFoldType; + for (var i = row + 1; i <= fold.end.row; i++) { + if (!gutter.$annotations[i]) + continue; + for (var j = 0; j < gutter.$annotations[i].text.length; j++) { + var annotationType = gutter.$annotations[i].type[j]; + annotationsInFold[annotationType].push(gutter.$annotations[i].text[j]); + if (annotationType === "error") { + mostSevereAnnotationInFoldType = "error_fold"; + continue; + } + if (annotationType === "warning") { + mostSevereAnnotationInFoldType = "warning_fold"; + continue; + } + } + } + if (mostSevereAnnotationInFoldType === "error_fold" || mostSevereAnnotationInFoldType === "warning_fold") { + var summaryFoldedAnnotations = "".concat(GutterTooltip.annotationsToSummaryString(annotationsInFold), " in folded code."); + annotation.displayText.push(summaryFoldedAnnotations); + annotation.type.push(mostSevereAnnotationInFoldType); + } + } + if (annotation.displayText.length === 0) + return this.hide(); + var annotationMessages = { + error: [], + warning: [], + info: [] + }; + var iconClassName = gutter.$useSvgGutterIcons ? "ace_icon_svg" : "ace_icon"; + for (var i = 0; i < annotation.displayText.length; i++) { + var lineElement = dom.createElement("span"); + var iconElement = dom.createElement("span"); + (_a = iconElement.classList).add.apply(_a, ["ace_".concat(annotation.type[i]), iconClassName]); + iconElement.setAttribute("aria-label", "".concat(GutterTooltip.annotationLabels[annotation.type[i].replace("_fold", "")].singular)); + iconElement.setAttribute("role", "img"); + iconElement.appendChild(dom.createTextNode(" ")); + lineElement.appendChild(iconElement); + lineElement.appendChild(dom.createTextNode(annotation.displayText[i])); + lineElement.appendChild(dom.createElement("br")); + annotationMessages[annotation.type[i].replace("_fold", "")].push(lineElement); + } + var tooltipElement = this.getElement(); + dom.removeChildren(tooltipElement); + annotationMessages.error.forEach(function(el) { + return tooltipElement.appendChild(el); + }); + annotationMessages.warning.forEach(function(el) { + return tooltipElement.appendChild(el); + }); + annotationMessages.info.forEach(function(el) { + return tooltipElement.appendChild(el); + }); + tooltipElement.setAttribute("aria-live", "polite"); + if (!this.isOpen) { + this.setTheme(this.editor.renderer.theme); + this.setClassName("ace_gutter-tooltip"); + } + this.show(); + this.editor._signal("showGutterTooltip", this); + }; + GutterTooltip.prototype.hideTooltip = function() { + this.$element.removeAttribute("aria-live"); + this.hide(); + this.editor._signal("hideGutterTooltip", this); + }; + GutterTooltip.annotationsToSummaryString = function(annotations) { + var e_1, _a; + var summary = []; + var annotationTypes = ['error', 'warning', 'info']; + try { + for (var annotationTypes_1 = __values(annotationTypes), annotationTypes_1_1 = annotationTypes_1.next(); !annotationTypes_1_1.done; annotationTypes_1_1 = annotationTypes_1.next()) { + var annotationType = annotationTypes_1_1.value; + if (!annotations[annotationType].length) + continue; + var label = annotations[annotationType].length === 1 ? GutterTooltip.annotationLabels[annotationType].singular : GutterTooltip.annotationLabels[annotationType].plural; + summary.push("".concat(annotations[annotationType].length, " ").concat(label)); + } + } catch (e_1_1) { + e_1 = { + error: e_1_1 + }; + } finally { + try { + if (annotationTypes_1_1 && !annotationTypes_1_1.done && (_a = annotationTypes_1.return)) _a.call(annotationTypes_1); + } finally { + if (e_1) throw e_1.error; + } + } + return summary.join(", "); + }; + return GutterTooltip; + }(Tooltip)); + exports.GutterTooltip = GutterTooltip; + +}); + +define("ace/mouse/mouse_event", ["require", "exports", "module", "ace/lib/event", "ace/lib/useragent"], function(require, exports, module) { + "use strict"; + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + var MouseEvent = /** @class */ (function() { + function MouseEvent(domEvent, editor) { + this.speed; + this.wheelX; + this.wheelY; + this.domEvent = domEvent; + this.editor = editor; + this.x = this.clientX = domEvent.clientX; + this.y = this.clientY = domEvent.clientY; + this.$pos = null; + this.$inSelection = null; + this.propagationStopped = false; + this.defaultPrevented = false; + } + MouseEvent.prototype.stopPropagation = function() { + event.stopPropagation(this.domEvent); + this.propagationStopped = true; + }; + MouseEvent.prototype.preventDefault = function() { + event.preventDefault(this.domEvent); + this.defaultPrevented = true; + }; + MouseEvent.prototype.stop = function() { + this.stopPropagation(); + this.preventDefault(); + }; + MouseEvent.prototype.getDocumentPosition = function() { + if (this.$pos) + return this.$pos; + this.$pos = this.editor.renderer.screenToTextCoordinates(this.clientX, this.clientY); + return this.$pos; + }; + MouseEvent.prototype.getGutterRow = function() { + var documentRow = this.getDocumentPosition().row; + var screenRow = this.editor.session.documentToScreenRow(documentRow, 0); + var screenTopRow = this.editor.session.documentToScreenRow(this.editor.renderer.$gutterLayer.$lines.get(0).row, 0); + return screenRow - screenTopRow; + }; + MouseEvent.prototype.inSelection = function() { + if (this.$inSelection !== null) + return this.$inSelection; + var editor = this.editor; + var selectionRange = editor.getSelectionRange(); + if (selectionRange.isEmpty()) + this.$inSelection = false; + else { + var pos = this.getDocumentPosition(); + this.$inSelection = selectionRange.contains(pos.row, pos.column); + } + return this.$inSelection; + }; + MouseEvent.prototype.getButton = function() { + return event.getButton(this.domEvent); + }; + MouseEvent.prototype.getShiftKey = function() { + return this.domEvent.shiftKey; + }; + MouseEvent.prototype.getAccelKey = function() { + return useragent.isMac ? this.domEvent.metaKey : this.domEvent.ctrlKey; + }; + return MouseEvent; + }()); + exports.MouseEvent = MouseEvent; + +}); + +define("ace/mouse/dragdrop_handler", ["require", "exports", "module", "ace/lib/dom", "ace/lib/event", "ace/lib/useragent"], function(require, exports, module) { + "use strict"; + var dom = require("../lib/dom"); + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + var AUTOSCROLL_DELAY = 200; + var SCROLL_CURSOR_DELAY = 200; + var SCROLL_CURSOR_HYSTERESIS = 5; + + function DragdropHandler(mouseHandler) { + var editor = mouseHandler.editor; + var dragImage = dom.createElement("div"); + dragImage.style.cssText = "top:-100px;position:absolute;z-index:2147483647;opacity:0.5"; + dragImage.textContent = "\xa0"; + var exports = ["dragWait", "dragWaitEnd", "startDrag", "dragReadyEnd", "onMouseDrag"]; + exports.forEach(function(x) { + mouseHandler[x] = this[x]; + }, this); + editor.on("mousedown", this.onMouseDown.bind(mouseHandler)); + var mouseTarget = editor.container; + var dragSelectionMarker, x, y; + var timerId, range; + var dragCursor, counter = 0; + var dragOperation; + var isInternal; + var autoScrollStartTime; + var cursorMovedTime; + var cursorPointOnCaretMoved; + this.onDragStart = function(e) { + if (this.cancelDrag || !mouseTarget.draggable) { + var self = this; + setTimeout(function() { + self.startSelect(); + self.captureMouse(e); + }, 0); + return e.preventDefault(); + } + range = editor.getSelectionRange(); + var dataTransfer = e.dataTransfer; + dataTransfer.effectAllowed = editor.getReadOnly() ? "copy" : "copyMove"; + editor.container.appendChild(dragImage); + dataTransfer.setDragImage && dataTransfer.setDragImage(dragImage, 0, 0); + setTimeout(function() { + editor.container.removeChild(dragImage); + }); + dataTransfer.clearData(); + dataTransfer.setData("Text", editor.session.getTextRange()); + isInternal = true; + this.setState("drag"); + }; + this.onDragEnd = function(e) { + mouseTarget.draggable = false; + isInternal = false; + this.setState(null); + if (!editor.getReadOnly()) { + var dropEffect = e.dataTransfer.dropEffect; + if (!dragOperation && dropEffect == "move") + editor.session.remove(editor.getSelectionRange()); + editor.$resetCursorStyle(); + } + this.editor.unsetStyle("ace_dragging"); + this.editor.renderer.setCursorStyle(""); + }; + this.onDragEnter = function(e) { + if (editor.getReadOnly() || !canAccept(e.dataTransfer)) + return; + x = e.clientX; + y = e.clientY; + if (!dragSelectionMarker) + addDragMarker(); + counter++; + e.dataTransfer.dropEffect = dragOperation = getDropEffect(e); + return event.preventDefault(e); + }; + this.onDragOver = function(e) { + if (editor.getReadOnly() || !canAccept(e.dataTransfer)) + return; + x = e.clientX; + y = e.clientY; + if (!dragSelectionMarker) { + addDragMarker(); + counter++; + } + if (onMouseMoveTimer !== null) + onMouseMoveTimer = null; + e.dataTransfer.dropEffect = dragOperation = getDropEffect(e); + return event.preventDefault(e); + }; + this.onDragLeave = function(e) { + counter--; + if (counter <= 0 && dragSelectionMarker) { + clearDragMarker(); + dragOperation = null; + return event.preventDefault(e); + } + }; + this.onDrop = function(e) { + if (!dragCursor) + return; + var dataTransfer = e.dataTransfer; + if (isInternal) { + switch (dragOperation) { + case "move": + if (range.contains(dragCursor.row, dragCursor.column)) { + range = { + start: dragCursor, + end: dragCursor + }; + } else { + range = editor.moveText(range, dragCursor); + } + break; + case "copy": + range = editor.moveText(range, dragCursor, true); + break; + } + } else { + var dropData = dataTransfer.getData('Text'); + range = { + start: dragCursor, + end: editor.session.insert(dragCursor, dropData) + }; + editor.focus(); + dragOperation = null; + } + clearDragMarker(); + return event.preventDefault(e); + }; + event.addListener(mouseTarget, "dragstart", this.onDragStart.bind(mouseHandler), editor); + event.addListener(mouseTarget, "dragend", this.onDragEnd.bind(mouseHandler), editor); + event.addListener(mouseTarget, "dragenter", this.onDragEnter.bind(mouseHandler), editor); + event.addListener(mouseTarget, "dragover", this.onDragOver.bind(mouseHandler), editor); + event.addListener(mouseTarget, "dragleave", this.onDragLeave.bind(mouseHandler), editor); + event.addListener(mouseTarget, "drop", this.onDrop.bind(mouseHandler), editor); + + function scrollCursorIntoView(cursor, prevCursor) { + var now = Date.now(); + var vMovement = !prevCursor || cursor.row != prevCursor.row; + var hMovement = !prevCursor || cursor.column != prevCursor.column; + if (!cursorMovedTime || vMovement || hMovement) { + editor.moveCursorToPosition(cursor); + cursorMovedTime = now; + cursorPointOnCaretMoved = { + x: x, + y: y + }; + } else { + var distance = calcDistance(cursorPointOnCaretMoved.x, cursorPointOnCaretMoved.y, x, y); + if (distance > SCROLL_CURSOR_HYSTERESIS) { + cursorMovedTime = null; + } else if (now - cursorMovedTime >= SCROLL_CURSOR_DELAY) { + editor.renderer.scrollCursorIntoView(); + cursorMovedTime = null; + } + } + } + + function autoScroll(cursor, prevCursor) { + var now = Date.now(); + var lineHeight = editor.renderer.layerConfig.lineHeight; + var characterWidth = editor.renderer.layerConfig.characterWidth; + var editorRect = editor.renderer.scroller.getBoundingClientRect(); + var offsets = { + x: { + left: x - editorRect.left, + right: editorRect.right - x + }, + y: { + top: y - editorRect.top, + bottom: editorRect.bottom - y + } + }; + var nearestXOffset = Math.min(offsets.x.left, offsets.x.right); + var nearestYOffset = Math.min(offsets.y.top, offsets.y.bottom); + var scrollCursor = { + row: cursor.row, + column: cursor.column + }; + if (nearestXOffset / characterWidth <= 2) { + scrollCursor.column += (offsets.x.left < offsets.x.right ? -3 : +2); + } + if (nearestYOffset / lineHeight <= 1) { + scrollCursor.row += (offsets.y.top < offsets.y.bottom ? -1 : +1); + } + var vScroll = cursor.row != scrollCursor.row; + var hScroll = cursor.column != scrollCursor.column; + var vMovement = !prevCursor || cursor.row != prevCursor.row; + if (vScroll || (hScroll && !vMovement)) { + if (!autoScrollStartTime) + autoScrollStartTime = now; + else if (now - autoScrollStartTime >= AUTOSCROLL_DELAY) + editor.renderer.scrollCursorIntoView(scrollCursor); + } else { + autoScrollStartTime = null; + } + } + + function onDragInterval() { + var prevCursor = dragCursor; + dragCursor = editor.renderer.screenToTextCoordinates(x, y); + scrollCursorIntoView(dragCursor, prevCursor); + autoScroll(dragCursor, prevCursor); + } + + function addDragMarker() { + range = editor.selection.toOrientedRange(); + dragSelectionMarker = editor.session.addMarker(range, "ace_selection", editor.getSelectionStyle()); + editor.clearSelection(); + if (editor.isFocused()) + editor.renderer.$cursorLayer.setBlinking(false); + clearInterval(timerId); + onDragInterval(); + timerId = setInterval(onDragInterval, 20); + counter = 0; + event.addListener(document, "mousemove", onMouseMove); + } + + function clearDragMarker() { + clearInterval(timerId); + editor.session.removeMarker(dragSelectionMarker); + dragSelectionMarker = null; + editor.selection.fromOrientedRange(range); + if (editor.isFocused() && !isInternal) + editor.$resetCursorStyle(); + range = null; + dragCursor = null; + counter = 0; + autoScrollStartTime = null; + cursorMovedTime = null; + event.removeListener(document, "mousemove", onMouseMove); + } + var onMouseMoveTimer = null; + + function onMouseMove() { + if (onMouseMoveTimer == null) { + onMouseMoveTimer = setTimeout(function() { + if (onMouseMoveTimer != null && dragSelectionMarker) + clearDragMarker(); + }, 20); + } + } + + function canAccept(dataTransfer) { + var types = dataTransfer.types; + return !types || Array.prototype.some.call(types, function(type) { + return type == 'text/plain' || type == 'Text'; + }); + } + + function getDropEffect(e) { + var copyAllowed = ['copy', 'copymove', 'all', 'uninitialized']; + var moveAllowed = ['move', 'copymove', 'linkmove', 'all', 'uninitialized']; + var copyModifierState = useragent.isMac ? e.altKey : e.ctrlKey; + var effectAllowed = "uninitialized"; + try { + effectAllowed = e.dataTransfer.effectAllowed.toLowerCase(); + } catch (e) {} + var dropEffect = "none"; + if (copyModifierState && copyAllowed.indexOf(effectAllowed) >= 0) + dropEffect = "copy"; + else if (moveAllowed.indexOf(effectAllowed) >= 0) + dropEffect = "move"; + else if (copyAllowed.indexOf(effectAllowed) >= 0) + dropEffect = "copy"; + return dropEffect; + } + } + (function() { + this.dragWait = function() { + var interval = Date.now() - this.mousedownEvent.time; + if (interval > this.editor.getDragDelay()) + this.startDrag(); + }; + this.dragWaitEnd = function() { + var target = this.editor.container; + target.draggable = false; + this.startSelect(this.mousedownEvent.getDocumentPosition()); + this.selectEnd(); + }; + this.dragReadyEnd = function(e) { + this.editor.$resetCursorStyle(); + this.editor.unsetStyle("ace_dragging"); + this.editor.renderer.setCursorStyle(""); + this.dragWaitEnd(); + }; + this.startDrag = function() { + this.cancelDrag = false; + var editor = this.editor; + var target = editor.container; + target.draggable = true; + editor.renderer.$cursorLayer.setBlinking(false); + editor.setStyle("ace_dragging"); + var cursorStyle = useragent.isWin ? "default" : "move"; + editor.renderer.setCursorStyle(cursorStyle); + this.setState("dragReady"); + }; + this.onMouseDrag = function(e) { + var target = this.editor.container; + if (useragent.isIE && this.state == "dragReady") { + var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); + if (distance > 3) + target.dragDrop(); + } + if (this.state === "dragWait") { + var distance = calcDistance(this.mousedownEvent.x, this.mousedownEvent.y, this.x, this.y); + if (distance > 0) { + target.draggable = false; + this.startSelect(this.mousedownEvent.getDocumentPosition()); + } + } + }; + this.onMouseDown = function(e) { + if (!this.$dragEnabled) + return; + this.mousedownEvent = e; + var editor = this.editor; + var inSelection = e.inSelection(); + var button = e.getButton(); + var clickCount = e.domEvent.detail || 1; + if (clickCount === 1 && button === 0 && inSelection) { + if (e.editor.inMultiSelectMode && (e.getAccelKey() || e.getShiftKey())) + return; + this.mousedownEvent.time = Date.now(); + var eventTarget = e.domEvent.target || e.domEvent.srcElement; + if ("unselectable" in eventTarget) + eventTarget.unselectable = "on"; + if (editor.getDragDelay()) { + if (useragent.isWebKit) { + this.cancelDrag = true; + var mouseTarget = editor.container; + mouseTarget.draggable = true; + } + this.setState("dragWait"); + } else { + this.startDrag(); + } + this.captureMouse(e, this.onMouseDrag.bind(this)); + e.defaultPrevented = true; + } + }; + }).call(DragdropHandler.prototype); + + function calcDistance(ax, ay, bx, by) { + return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); + } + exports.DragdropHandler = DragdropHandler; + +}); + +define("ace/mouse/touch_handler", ["require", "exports", "module", "ace/mouse/mouse_event", "ace/lib/event", "ace/lib/dom"], function(require, exports, module) { + "use strict"; + var MouseEvent = require("./mouse_event").MouseEvent; + var event = require("../lib/event"); + var dom = require("../lib/dom"); + exports.addTouchListeners = function(el, editor) { + var mode = "scroll"; + var startX; + var startY; + var touchStartT; + var lastT; + var longTouchTimer; + var animationTimer; + var animationSteps = 0; + var pos; + var clickCount = 0; + var vX = 0; + var vY = 0; + var pressed; + var contextMenu; + + function createContextMenu() { + var clipboard = window.navigator && window.navigator.clipboard; + var isOpen = false; + var updateMenu = function() { + var selected = editor.getCopyText(); + var hasUndo = editor.session.getUndoManager().hasUndo(); + contextMenu.replaceChild(dom.buildDom(isOpen ? ["span", + !selected && canExecuteCommand("selectall") && ["span", { + class: "ace_mobile-button", + action: "selectall" + }, "Select All"], + selected && canExecuteCommand("copy") && ["span", { + class: "ace_mobile-button", + action: "copy" + }, "Copy"], + selected && canExecuteCommand("cut") && ["span", { + class: "ace_mobile-button", + action: "cut" + }, "Cut"], + clipboard && canExecuteCommand("paste") && ["span", { + class: "ace_mobile-button", + action: "paste" + }, "Paste"], + hasUndo && canExecuteCommand("undo") && ["span", { + class: "ace_mobile-button", + action: "undo" + }, "Undo"], + canExecuteCommand("find") && ["span", { + class: "ace_mobile-button", + action: "find" + }, "Find"], + canExecuteCommand("openCommandPalette") && ["span", { + class: "ace_mobile-button", + action: "openCommandPalette" + }, "Palette"] + ] : ["span"]), contextMenu.firstChild); + }; + var canExecuteCommand = function( /** @type {string} */ cmd) { + return editor.commands.canExecute(cmd, editor); + }; + var handleClick = function(e) { + var action = e.target.getAttribute("action"); + if (action == "more" || !isOpen) { + isOpen = !isOpen; + return updateMenu(); + } + if (action == "paste") { + clipboard.readText().then(function(text) { + editor.execCommand(action, text); + }); + } else if (action) { + if (action == "cut" || action == "copy") { + if (clipboard) + clipboard.writeText(editor.getCopyText()); + else + document.execCommand("copy"); + } + editor.execCommand(action); + } + contextMenu.firstChild.style.display = "none"; + isOpen = false; + if (action != "openCommandPalette") + editor.focus(); + }; + contextMenu = dom.buildDom(["div", + { + class: "ace_mobile-menu", + ontouchstart: function(e) { + mode = "menu"; + e.stopPropagation(); + e.preventDefault(); + editor.textInput.focus(); + }, + ontouchend: function(e) { + e.stopPropagation(); + e.preventDefault(); + handleClick(e); + }, + onclick: handleClick + }, + ["span"], + ["span", { + class: "ace_mobile-button", + action: "more" + }, "..."] + ], editor.container); + } + + function showContextMenu() { + if (!editor.getOption("enableMobileMenu")) { + if (contextMenu) { + hideContextMenu(); + } + return; + } + if (!contextMenu) + createContextMenu(); + var cursor = editor.selection.cursor; + var pagePos = editor.renderer.textToScreenCoordinates(cursor.row, cursor.column); + var leftOffset = editor.renderer.textToScreenCoordinates(0, 0).pageX; + var scrollLeft = editor.renderer.scrollLeft; + var rect = editor.container.getBoundingClientRect(); + contextMenu.style.top = pagePos.pageY - rect.top - 3 + "px"; + if (pagePos.pageX - rect.left < rect.width - 70) { + contextMenu.style.left = ""; + contextMenu.style.right = "10px"; + } else { + contextMenu.style.right = ""; + contextMenu.style.left = leftOffset + scrollLeft - rect.left + "px"; + } + contextMenu.style.display = ""; + contextMenu.firstChild.style.display = "none"; + editor.on("input", hideContextMenu); + } + + function hideContextMenu(e) { + if (contextMenu) + contextMenu.style.display = "none"; + editor.off("input", hideContextMenu); + } + + function handleLongTap() { + longTouchTimer = null; + clearTimeout(longTouchTimer); + var range = editor.selection.getRange(); + var inSelection = range.contains(pos.row, pos.column); + if (range.isEmpty() || !inSelection) { + editor.selection.moveToPosition(pos); + editor.selection.selectWord(); + } + mode = "wait"; + showContextMenu(); + } + + function switchToSelectionMode() { + longTouchTimer = null; + clearTimeout(longTouchTimer); + editor.selection.moveToPosition(pos); + var range = clickCount >= 2 ? + editor.selection.getLineRange(pos.row) : + editor.session.getBracketRange(pos); + if (range && !range.isEmpty()) { + editor.selection.setRange(range); + } else { + editor.selection.selectWord(); + } + mode = "wait"; + } + event.addListener(el, "contextmenu", function(e) { + if (!pressed) + return; + var textarea = editor.textInput.getElement(); + textarea.focus(); + }, editor); + event.addListener(el, "touchstart", function(e) { + var touches = e.touches; + if (longTouchTimer || touches.length > 1) { + clearTimeout(longTouchTimer); + longTouchTimer = null; + touchStartT = -1; + mode = "zoom"; + return; + } + pressed = editor.$mouseHandler.isMousePressed = true; + var h = editor.renderer.layerConfig.lineHeight; + var w = editor.renderer.layerConfig.lineHeight; + var t = e.timeStamp; + lastT = t; + var touchObj = touches[0]; + var x = touchObj.clientX; + var y = touchObj.clientY; + if (Math.abs(startX - x) + Math.abs(startY - y) > h) + touchStartT = -1; + startX = e.clientX = x; + startY = e.clientY = y; + vX = vY = 0; + var ev = new MouseEvent(e, editor); + pos = ev.getDocumentPosition(); + if (t - touchStartT < 500 && touches.length == 1 && !animationSteps) { + clickCount++; + e.preventDefault(); + e.button = 0; + switchToSelectionMode(); + } else { + clickCount = 0; + var cursor = editor.selection.cursor; + var anchor = editor.selection.isEmpty() ? cursor : editor.selection.anchor; + var cursorPos = editor.renderer.$cursorLayer.getPixelPosition(cursor, true); + var anchorPos = editor.renderer.$cursorLayer.getPixelPosition(anchor, true); + var rect = editor.renderer.scroller.getBoundingClientRect(); + var offsetTop = editor.renderer.layerConfig.offset; + var offsetLeft = editor.renderer.scrollLeft; + var weightedDistance = function(x, y) { + x = x / w; + y = y / h - 0.75; + return x * x + y * y; + }; + if (e.clientX < rect.left) { + mode = "zoom"; + return; + } + var diff1 = weightedDistance(e.clientX - rect.left - cursorPos.left + offsetLeft, e.clientY - rect.top - cursorPos.top + offsetTop); + var diff2 = weightedDistance(e.clientX - rect.left - anchorPos.left + offsetLeft, e.clientY - rect.top - anchorPos.top + offsetTop); + if (diff1 < 3.5 && diff2 < 3.5) + mode = diff1 > diff2 ? "cursor" : "anchor"; + if (diff2 < 3.5) + mode = "anchor"; + else if (diff1 < 3.5) + mode = "cursor"; + else + mode = "scroll"; + longTouchTimer = setTimeout(handleLongTap, 450); + } + touchStartT = t; + }, editor); + event.addListener(el, "touchend", function(e) { + pressed = editor.$mouseHandler.isMousePressed = false; + if (animationTimer) + clearInterval(animationTimer); + if (mode == "zoom") { + mode = ""; + animationSteps = 0; + } else if (longTouchTimer) { + editor.selection.moveToPosition(pos); + animationSteps = 0; + showContextMenu(); + } else if (mode == "scroll") { + animate(); + hideContextMenu(); + } else { + showContextMenu(); + } + clearTimeout(longTouchTimer); + longTouchTimer = null; + }, editor); + event.addListener(el, "touchmove", function(e) { + if (longTouchTimer) { + clearTimeout(longTouchTimer); + longTouchTimer = null; + } + var touches = e.touches; + if (touches.length > 1 || mode == "zoom") + return; + var touchObj = touches[0]; + var wheelX = startX - touchObj.clientX; + var wheelY = startY - touchObj.clientY; + if (mode == "wait") { + if (wheelX * wheelX + wheelY * wheelY > 4) + mode = "cursor"; + else + return e.preventDefault(); + } + startX = touchObj.clientX; + startY = touchObj.clientY; + e.clientX = touchObj.clientX; + e.clientY = touchObj.clientY; + var t = e.timeStamp; + var dt = t - lastT; + lastT = t; + if (mode == "scroll") { + var mouseEvent = new MouseEvent(e, editor); + mouseEvent.speed = 1; + mouseEvent.wheelX = wheelX; + mouseEvent.wheelY = wheelY; + if (10 * Math.abs(wheelX) < Math.abs(wheelY)) + wheelX = 0; + if (10 * Math.abs(wheelY) < Math.abs(wheelX)) + wheelY = 0; + if (dt != 0) { + vX = wheelX / dt; + vY = wheelY / dt; + } + editor._emit("mousewheel", mouseEvent); + if (!mouseEvent.propagationStopped) { + vX = vY = 0; + } + } else { + var ev = new MouseEvent(e, editor); + var pos = ev.getDocumentPosition(); + if (mode == "cursor") + editor.selection.moveCursorToPosition(pos); + else if (mode == "anchor") + editor.selection.setSelectionAnchor(pos.row, pos.column); + editor.renderer.scrollCursorIntoView(pos); + e.preventDefault(); + } + }, editor); + + function animate() { + animationSteps += 60; + animationTimer = setInterval(function() { + if (animationSteps-- <= 0) { + clearInterval(animationTimer); + animationTimer = null; + } + if (Math.abs(vX) < 0.01) + vX = 0; + if (Math.abs(vY) < 0.01) + vY = 0; + if (animationSteps < 20) + vX = 0.9 * vX; + if (animationSteps < 20) + vY = 0.9 * vY; + var oldScrollTop = editor.session.getScrollTop(); + editor.renderer.scrollBy(10 * vX, 10 * vY); + if (oldScrollTop == editor.session.getScrollTop()) + animationSteps = 0; + }, 10); + } + }; + +}); + +define("ace/mouse/mouse_handler", ["require", "exports", "module", "ace/lib/event", "ace/lib/useragent", "ace/mouse/default_handlers", "ace/mouse/default_gutter_handler", "ace/mouse/mouse_event", "ace/mouse/dragdrop_handler", "ace/mouse/touch_handler", "ace/config"], function(require, exports, module) { + "use strict"; + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + var DefaultHandlers = require("./default_handlers").DefaultHandlers; + var DefaultGutterHandler = require("./default_gutter_handler").GutterHandler; + var MouseEvent = require("./mouse_event").MouseEvent; + var DragdropHandler = require("./dragdrop_handler").DragdropHandler; + var addTouchListeners = require("./touch_handler").addTouchListeners; + var config = require("../config"); + var MouseHandler = /** @class */ (function() { + function MouseHandler(editor) { + this.$dragDelay; + this.$dragEnabled; + this.$mouseMoved; + this.mouseEvent; + this.$focusTimeout; + var _self = this; + this.editor = editor; + new DefaultHandlers(this); + new DefaultGutterHandler(this); + new DragdropHandler(this); + var focusEditor = function(e) { + var windowBlurred = !document.hasFocus || !document.hasFocus() || + !editor.isFocused() && document.activeElement == (editor.textInput && editor.textInput.getElement()); + if (windowBlurred) + window.focus(); + editor.focus(); + setTimeout(function() { + if (!editor.isFocused()) + editor.focus(); + }); + }; + var mouseTarget = editor.renderer.getMouseEventTarget(); + event.addListener(mouseTarget, "click", this.onMouseEvent.bind(this, "click"), editor); + event.addListener(mouseTarget, "mousemove", this.onMouseMove.bind(this, "mousemove"), editor); + event.addMultiMouseDownListener([ + mouseTarget, + editor.renderer.scrollBarV && editor.renderer.scrollBarV.inner, + editor.renderer.scrollBarH && editor.renderer.scrollBarH.inner, + editor.textInput && editor.textInput.getElement() + ].filter(Boolean), [400, 300, 250], this, "onMouseEvent", editor); + event.addMouseWheelListener(editor.container, this.onMouseWheel.bind(this, "mousewheel"), editor); + addTouchListeners(editor.container, editor); + var gutterEl = editor.renderer.$gutter; + event.addListener(gutterEl, "mousedown", this.onMouseEvent.bind(this, "guttermousedown"), editor); + event.addListener(gutterEl, "click", this.onMouseEvent.bind(this, "gutterclick"), editor); + event.addListener(gutterEl, "dblclick", this.onMouseEvent.bind(this, "gutterdblclick"), editor); + event.addListener(gutterEl, "mousemove", this.onMouseEvent.bind(this, "guttermousemove"), editor); + event.addListener(mouseTarget, "mousedown", focusEditor, editor); + event.addListener(gutterEl, "mousedown", focusEditor, editor); + if (useragent.isIE && editor.renderer.scrollBarV) { + event.addListener(editor.renderer.scrollBarV.element, "mousedown", focusEditor, editor); + event.addListener(editor.renderer.scrollBarH.element, "mousedown", focusEditor, editor); + } + editor.on("mousemove", function(e) { + if (_self.state || _self.$dragDelay || !_self.$dragEnabled) + return; + var character = editor.renderer.screenToTextCoordinates(e.x, e.y); + var range = editor.session.selection.getRange(); + var renderer = editor.renderer; + if (!range.isEmpty() && range.insideStart(character.row, character.column)) { + renderer.setCursorStyle("default"); + } else { + renderer.setCursorStyle(""); + } + }, //@ts-expect-error TODO: seems mistyping - should be boolean + editor); + } + MouseHandler.prototype.onMouseEvent = function(name, e) { + if (!this.editor.session) + return; + this.editor._emit(name, new MouseEvent(e, this.editor)); + }; + MouseHandler.prototype.onMouseMove = function(name, e) { + var listeners = this.editor._eventRegistry && this.editor._eventRegistry.mousemove; + if (!listeners || !listeners.length) + return; + this.editor._emit(name, new MouseEvent(e, this.editor)); + }; + MouseHandler.prototype.onMouseWheel = function(name, e) { + var mouseEvent = new MouseEvent(e, this.editor); + mouseEvent.speed = this.$scrollSpeed * 2; + mouseEvent.wheelX = e.wheelX; + mouseEvent.wheelY = e.wheelY; + this.editor._emit(name, mouseEvent); + }; + MouseHandler.prototype.setState = function(state) { + this.state = state; + }; + MouseHandler.prototype.captureMouse = function(ev, mouseMoveHandler) { + this.x = ev.x; + this.y = ev.y; + this.isMousePressed = true; + var editor = this.editor; + var renderer = this.editor.renderer; + renderer.$isMousePressed = true; + var self = this; + var onMouseMove = function(e) { + if (!e) + return; + if (useragent.isWebKit && !e.which && self.releaseMouse) + return self.releaseMouse(); + self.x = e.clientX; + self.y = e.clientY; + mouseMoveHandler && mouseMoveHandler(e); + self.mouseEvent = new MouseEvent(e, self.editor); + self.$mouseMoved = true; + }; + var onCaptureEnd = function(e) { + editor.off("beforeEndOperation", onOperationEnd); + clearInterval(timerId); + if (editor.session) + onCaptureInterval(); + self[self.state + "End"] && self[self.state + "End"](e); + self.state = ""; + self.isMousePressed = renderer.$isMousePressed = false; + if (renderer.$keepTextAreaAtCursor) + renderer.$moveTextAreaToCursor(); + self.$onCaptureMouseMove = self.releaseMouse = null; + e && self.onMouseEvent("mouseup", e); + editor.endOperation(); + }; + var onCaptureInterval = function() { + self[self.state] && self[self.state](); + self.$mouseMoved = false; + }; + if (useragent.isOldIE && ev.domEvent.type == "dblclick") { + return setTimeout(function() { + onCaptureEnd(ev); + }); + } + var onOperationEnd = function(e) { + if (!self.releaseMouse) + return; + if (editor.curOp.command.name && editor.curOp.selectionChanged) { + self[self.state + "End"] && self[self.state + "End"](); + self.state = ""; + self.releaseMouse(); + } + }; + editor.on("beforeEndOperation", onOperationEnd); + editor.startOperation({ + command: { + name: "mouse" + } + }); + self.$onCaptureMouseMove = onMouseMove; + self.releaseMouse = event.capture(this.editor.container, onMouseMove, onCaptureEnd); + var timerId = setInterval(onCaptureInterval, 20); + }; + MouseHandler.prototype.cancelContextMenu = function() { + var stop = function(e) { + if (e && e.domEvent && e.domEvent.type != "contextmenu") + return; + this.editor.off("nativecontextmenu", stop); + if (e && e.domEvent) + event.stopEvent(e.domEvent); + }.bind(this); + setTimeout(stop, 10); + this.editor.on("nativecontextmenu", stop); + }; + MouseHandler.prototype.destroy = function() { + if (this.releaseMouse) + this.releaseMouse(); + }; + return MouseHandler; + }()); + MouseHandler.prototype.releaseMouse = null; + config.defineOptions(MouseHandler.prototype, "mouseHandler", { + scrollSpeed: { + initialValue: 2 + }, + dragDelay: { + initialValue: (useragent.isMac ? 150 : 0) + }, + dragEnabled: { + initialValue: true + }, + focusTimeout: { + initialValue: 0 + }, + tooltipFollowsMouse: { + initialValue: true + } + }); + exports.MouseHandler = MouseHandler; + +}); + +define("ace/mouse/fold_handler", ["require", "exports", "module", "ace/lib/dom"], function(require, exports, module) { + "use strict"; + var dom = require("../lib/dom"); + var FoldHandler = /** @class */ (function() { + function FoldHandler(editor) { + editor.on("click", function(e) { + var position = e.getDocumentPosition(); + var session = editor.session; + var fold = session.getFoldAt(position.row, position.column, 1); + if (fold) { + if (e.getAccelKey()) + session.removeFold(fold); + else + session.expandFold(fold); + e.stop(); + } + var target = e.domEvent && e.domEvent.target; + if (target && dom.hasCssClass(target, "ace_inline_button")) { + if (dom.hasCssClass(target, "ace_toggle_wrap")) { + session.setOption("wrap", !session.getUseWrapMode()); + editor.renderer.scrollCursorIntoView(); + } + } + }); + editor.on("gutterclick", function(e) { + var gutterRegion = editor.renderer.$gutterLayer.getRegion(e); + if (gutterRegion == "foldWidgets") { + var row = e.getDocumentPosition().row; + var session = editor.session; + if (session.foldWidgets && session.foldWidgets[row]) + editor.session.onFoldWidgetClick(row, e); + if (!editor.isFocused()) + editor.focus(); + e.stop(); + } + }); + editor.on("gutterdblclick", function(e) { + var gutterRegion = editor.renderer.$gutterLayer.getRegion(e); + if (gutterRegion == "foldWidgets") { + var row = e.getDocumentPosition().row; + var session = editor.session; + var data = session.getParentFoldRangeData(row, true); + var range = data.range || data.firstRange; + if (range) { + row = range.start.row; + var fold = session.getFoldAt(row, session.getLine(row).length, 1); + if (fold) { + session.removeFold(fold); + } else { + session.addFold("...", range); + editor.renderer.scrollCursorIntoView({ + row: range.start.row, + column: 0 + }); + } + } + e.stop(); + } + }); + } + return FoldHandler; + }()); + exports.FoldHandler = FoldHandler; + +}); + +define("ace/keyboard/keybinding", ["require", "exports", "module", "ace/lib/keys", "ace/lib/event"], function(require, exports, module) { + "use strict"; + var keyUtil = require("../lib/keys"); + var event = require("../lib/event"); + var KeyBinding = /** @class */ (function() { + function KeyBinding(editor) { + this.$editor = editor; + this.$data = { + editor: editor + }; + this.$handlers = []; + this.setDefaultHandler(editor.commands); + } + KeyBinding.prototype.setDefaultHandler = function(kb) { + this.removeKeyboardHandler(this.$defaultHandler); + this.$defaultHandler = kb; + this.addKeyboardHandler(kb, 0); + }; + KeyBinding.prototype.setKeyboardHandler = function(kb) { + var h = this.$handlers; + if (h[h.length - 1] == kb) + return; + while (h[h.length - 1] && h[h.length - 1] != this.$defaultHandler) + this.removeKeyboardHandler(h[h.length - 1]); + this.addKeyboardHandler(kb, 1); + }; + KeyBinding.prototype.addKeyboardHandler = function(kb, pos) { + if (!kb) + return; + if (typeof kb == "function" && !kb.handleKeyboard) + kb.handleKeyboard = kb; + var i = this.$handlers.indexOf(kb); + if (i != -1) + this.$handlers.splice(i, 1); + if (pos == undefined) + this.$handlers.push(kb); + else + this.$handlers.splice(pos, 0, kb); + if (i == -1 && kb.attach) + kb.attach(this.$editor); + }; + KeyBinding.prototype.removeKeyboardHandler = function(kb) { + var i = this.$handlers.indexOf(kb); + if (i == -1) + return false; + this.$handlers.splice(i, 1); + kb.detach && kb.detach(this.$editor); + return true; + }; + KeyBinding.prototype.getKeyboardHandler = function() { + return this.$handlers[this.$handlers.length - 1]; + }; + KeyBinding.prototype.getStatusText = function() { + var data = this.$data; + var editor = data.editor; + return this.$handlers.map(function(h) { + return h.getStatusText && h.getStatusText(editor, data) || ""; + }).filter(Boolean).join(" "); + }; + KeyBinding.prototype.$callKeyboardHandlers = function(hashId, keyString, keyCode, e) { + var toExecute; + var success = false; + var commands = this.$editor.commands; + for (var i = this.$handlers.length; i--;) { + toExecute = this.$handlers[i].handleKeyboard( + this.$data, hashId, keyString, keyCode, e); + if (!toExecute || !toExecute.command) + continue; + if (toExecute.command == "null") { + success = true; + } else { + success = commands.exec(toExecute.command, this.$editor, toExecute.args, e); + } + if (success && e && hashId != -1 && + toExecute["passEvent"] != true && toExecute.command["passEvent"] != true) { + event.stopEvent(e); + } + if (success) + break; + } + if (!success && hashId == -1) { + toExecute = { + command: "insertstring" + }; + success = commands.exec("insertstring", this.$editor, keyString); + } + if (success && this.$editor._signal) + this.$editor._signal("keyboardActivity", toExecute); + return success; + }; + KeyBinding.prototype.onCommandKey = function(e, hashId, keyCode) { + var keyString = keyUtil.keyCodeToString(keyCode); + return this.$callKeyboardHandlers(hashId, keyString, keyCode, e); + }; + KeyBinding.prototype.onTextInput = function(text) { + return this.$callKeyboardHandlers(-1, text); + }; + return KeyBinding; + }()); + exports.KeyBinding = KeyBinding; + +}); + +define("ace/lib/bidiutil", ["require", "exports", "module"], function(require, exports, module) { + "use strict"; + var ArabicAlefBetIntervalsBegine = ['\u0621', '\u0641']; + var ArabicAlefBetIntervalsEnd = ['\u063A', '\u064a']; + var dir = 0, + hiLevel = 0; + var lastArabic = false, + hasUBAT_AL = false, + hasUBAT_B = false, + hasUBAT_S = false, + hasBlockSep = false, + hasSegSep = false; + var impTab_LTR = [ + [0, 3, 0, 1, 0, 0, 0], + [0, 3, 0, 1, 2, 2, 0], + [0, 3, 0, 0x11, 2, 0, 1], + [0, 3, 5, 5, 4, 1, 0], + [0, 3, 0x15, 0x15, 4, 0, 1], + [0, 3, 5, 5, 4, 2, 0] + ]; + var impTab_RTL = [ + [2, 0, 1, 1, 0, 1, 0], + [2, 0, 1, 1, 0, 2, 0], + [2, 0, 2, 1, 3, 2, 0], + [2, 0, 2, 0x21, 3, 1, 1] + ]; + var LTR = 0, + RTL = 1; + var L = 0; + var R = 1; + var EN = 2; + var AN = 3; + var ON = 4; + var B = 5; + var S = 6; + var AL = 7; + var WS = 8; + var CS = 9; + var ES = 10; + var ET = 11; + var NSM = 12; + var LRE = 13; + var RLE = 14; + var PDF = 15; + var LRO = 16; + var RLO = 17; + var BN = 18; + var UnicodeTBL00 = [ + BN, BN, BN, BN, BN, BN, BN, BN, BN, S, B, S, WS, B, BN, BN, + BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, B, B, B, S, + WS, ON, ON, ET, ET, ET, ON, ON, ON, ON, ON, ES, CS, ES, CS, CS, + EN, EN, EN, EN, EN, EN, EN, EN, EN, EN, CS, ON, ON, ON, ON, ON, + ON, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, + L, L, L, L, L, L, L, L, L, L, L, ON, ON, ON, ON, ON, + ON, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, + L, L, L, L, L, L, L, L, L, L, L, ON, ON, ON, ON, BN, + BN, BN, BN, BN, BN, B, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, + BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, BN, + CS, ON, ET, ET, ET, ET, ON, ON, ON, ON, L, ON, ON, BN, ON, ON, + ET, ET, EN, EN, ON, L, ON, ON, ON, EN, L, ON, ON, ON, ON, ON + ]; + var UnicodeTBL20 = [ + WS, WS, WS, WS, WS, WS, WS, WS, WS, WS, WS, BN, BN, BN, L, R, + ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, + ON, ON, ON, ON, ON, ON, ON, ON, WS, B, LRE, RLE, PDF, LRO, RLO, CS, + ET, ET, ET, ET, ET, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, + ON, ON, ON, ON, CS, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, + ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, ON, WS + ]; + + function _computeLevels(chars, levels, len, charTypes) { + var impTab = dir ? impTab_RTL : impTab_LTR, + prevState = null, + newClass = null, + newLevel = null, + newState = 0, + action = null, + cond = null, + condPos = -1, + i = null, + ix = null, + classes = []; + if (!charTypes) { + for (i = 0, charTypes = []; i < len; i++) { + charTypes[i] = _getCharacterType(chars[i]); + } + } + hiLevel = dir; + lastArabic = false; + hasUBAT_AL = false; + hasUBAT_B = false; + hasUBAT_S = false; + for (ix = 0; ix < len; ix++) { + prevState = newState; + classes[ix] = newClass = _getCharClass(chars, charTypes, classes, ix); + newState = impTab[prevState][newClass]; + action = newState & 0xF0; + newState &= 0x0F; + levels[ix] = newLevel = impTab[newState][5]; + if (action > 0) { + if (action == 0x10) { + for (i = condPos; i < ix; i++) { + levels[i] = 1; + } + condPos = -1; + } else { + condPos = -1; + } + } + cond = impTab[newState][6]; + if (cond) { + if (condPos == -1) { + condPos = ix; + } + } else { + if (condPos > -1) { + for (i = condPos; i < ix; i++) { + levels[i] = newLevel; + } + condPos = -1; + } + } + if (charTypes[ix] == B) { + levels[ix] = 0; + } + hiLevel |= newLevel; + } + if (hasUBAT_S) { + for (i = 0; i < len; i++) { + if (charTypes[i] == S) { + levels[i] = dir; + for (var j = i - 1; j >= 0; j--) { + if (charTypes[j] == WS) { + levels[j] = dir; + } else { + break; + } + } + } + } + } + } + + function _invertLevel(lev, levels, _array) { + if (hiLevel < lev) { + return; + } + if (lev == 1 && dir == RTL && !hasUBAT_B) { + _array.reverse(); + return; + } + var len = _array.length, + start = 0, + end, lo, hi, tmp; + while (start < len) { + if (levels[start] >= lev) { + end = start + 1; + while (end < len && levels[end] >= lev) { + end++; + } + for (lo = start, hi = end - 1; lo < hi; lo++, hi--) { + tmp = _array[lo]; + _array[lo] = _array[hi]; + _array[hi] = tmp; + } + start = end; + } + start++; + } + } + + function _getCharClass(chars, types, classes, ix) { + var cType = types[ix], + wType, nType, len, i; + switch (cType) { + case L: + case R: + lastArabic = false; + case ON: + case AN: + return cType; + case EN: + return lastArabic ? AN : EN; + case AL: + lastArabic = true; + hasUBAT_AL = true; + return R; + case WS: + return ON; + case CS: + if (ix < 1 || (ix + 1) >= types.length || + ((wType = classes[ix - 1]) != EN && wType != AN) || + ((nType = types[ix + 1]) != EN && nType != AN)) { + return ON; + } + if (lastArabic) { + nType = AN; + } + return nType == wType ? nType : ON; + case ES: + wType = ix > 0 ? classes[ix - 1] : B; + if (wType == EN && (ix + 1) < types.length && types[ix + 1] == EN) { + return EN; + } + return ON; + case ET: + if (ix > 0 && classes[ix - 1] == EN) { + return EN; + } + if (lastArabic) { + return ON; + } + i = ix + 1; + len = types.length; + while (i < len && types[i] == ET) { + i++; + } + if (i < len && types[i] == EN) { + return EN; + } + return ON; + case NSM: + len = types.length; + i = ix + 1; + while (i < len && types[i] == NSM) { + i++; + } + if (i < len) { + var c = chars[ix], + rtlCandidate = (c >= 0x0591 && c <= 0x08FF) || c == 0xFB1E; + wType = types[i]; + if (rtlCandidate && (wType == R || wType == AL)) { + return R; + } + } + if (ix < 1 || (wType = types[ix - 1]) == B) { + return ON; + } + return classes[ix - 1]; + case B: + lastArabic = false; + hasUBAT_B = true; + return dir; + case S: + hasUBAT_S = true; + return ON; + case LRE: + case RLE: + case LRO: + case RLO: + case PDF: + lastArabic = false; + case BN: + return ON; + } + } + + function _getCharacterType(ch) { + var uc = ch.charCodeAt(0), + hi = uc >> 8; + if (hi == 0) { + return ((uc > 0x00BF) ? L : UnicodeTBL00[uc]); + } else if (hi == 5) { + return (/[\u0591-\u05f4]/.test(ch) ? R : L); + } else if (hi == 6) { + if (/[\u0610-\u061a\u064b-\u065f\u06d6-\u06e4\u06e7-\u06ed]/.test(ch)) + return NSM; + else if (/[\u0660-\u0669\u066b-\u066c]/.test(ch)) + return AN; + else if (uc == 0x066A) + return ET; + else if (/[\u06f0-\u06f9]/.test(ch)) + return EN; + else + return AL; + } else if (hi == 0x20 && uc <= 0x205F) { + return UnicodeTBL20[uc & 0xFF]; + } else if (hi == 0xFE) { + return (uc >= 0xFE70 ? AL : ON); + } + return ON; + } + + function _isArabicDiacritics(ch) { + return (ch >= '\u064b' && ch <= '\u0655'); + } + exports.L = L; + exports.R = R; + exports.EN = EN; + exports.ON_R = 3; + exports.AN = 4; + exports.R_H = 5; + exports.B = 6; + exports.RLE = 7; + exports.DOT = "\xB7"; + exports.doBidiReorder = function(text, textCharTypes, isRtl) { + if (text.length < 2) + return {}; + var chars = text.split(""), + logicalFromVisual = new Array(chars.length), + bidiLevels = new Array(chars.length), + levels = []; + dir = isRtl ? RTL : LTR; + _computeLevels(chars, levels, chars.length, textCharTypes); + for (var i = 0; i < logicalFromVisual.length; logicalFromVisual[i] = i, i++) + ; + _invertLevel(2, levels, logicalFromVisual); + _invertLevel(1, levels, logicalFromVisual); + for (var i = 0; i < logicalFromVisual.length - 1; i++) { //fix levels to reflect character width + if (textCharTypes[i] === AN) { + levels[i] = exports.AN; + } else if (levels[i] === R && ((textCharTypes[i] > AL && textCharTypes[i] < LRE) || + textCharTypes[i] === ON || textCharTypes[i] === BN)) { + levels[i] = exports.ON_R; + } else if ((i > 0 && chars[i - 1] === '\u0644') && /\u0622|\u0623|\u0625|\u0627/.test(chars[i])) { + levels[i - 1] = levels[i] = exports.R_H; + i++; + } + } + if (chars[chars.length - 1] === exports.DOT) + levels[chars.length - 1] = exports.B; + if (chars[0] === '\u202B') + levels[0] = exports.RLE; + for (var i = 0; i < logicalFromVisual.length; i++) { + bidiLevels[i] = levels[logicalFromVisual[i]]; + } + return { + 'logicalFromVisual': logicalFromVisual, + 'bidiLevels': bidiLevels + }; + }; + exports.hasBidiCharacters = function(text, textCharTypes) { + var ret = false; + for (var i = 0; i < text.length; i++) { + textCharTypes[i] = _getCharacterType(text.charAt(i)); + if (!ret && (textCharTypes[i] == R || textCharTypes[i] == AL || textCharTypes[i] == AN)) + ret = true; + } + return ret; + }; + exports.getVisualFromLogicalIdx = function(logIdx, rowMap) { + for (var i = 0; i < rowMap.logicalFromVisual.length; i++) { + if (rowMap.logicalFromVisual[i] == logIdx) + return i; + } + return 0; + }; + +}); + +define("ace/bidihandler", ["require", "exports", "module", "ace/lib/bidiutil", "ace/lib/lang"], function(require, exports, module) { + "use strict"; + var bidiUtil = require("./lib/bidiutil"); + var lang = require("./lib/lang"); + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\u202B]/; + var BidiHandler = /** @class */ (function() { + function BidiHandler(session) { + this.session = session; + this.bidiMap = {}; + this.currentRow = null; + this.bidiUtil = bidiUtil; + this.charWidths = []; + this.EOL = "\xAC"; + this.showInvisibles = true; + this.isRtlDir = false; + this.$isRtl = false; + this.line = ""; + this.wrapIndent = 0; + this.EOF = "\xB6"; + this.RLE = "\u202B"; + this.contentWidth = 0; + this.fontMetrics = null; + this.rtlLineOffset = 0; + this.wrapOffset = 0; + this.isMoveLeftOperation = false; + this.seenBidi = bidiRE.test(session.getValue()); + } + BidiHandler.prototype.isBidiRow = function(screenRow, docRow, splitIndex) { + if (!this.seenBidi) + return false; + if (screenRow !== this.currentRow) { + this.currentRow = screenRow; + this.updateRowLine(docRow, splitIndex); + this.updateBidiMap(); + } + return this.bidiMap.bidiLevels; + }; + BidiHandler.prototype.onChange = function(delta) { + if (!this.seenBidi) { + if (delta.action == "insert" && bidiRE.test(delta.lines.join("\n"))) { + this.seenBidi = true; + this.currentRow = null; + } + } else { + this.currentRow = null; + } + }; + BidiHandler.prototype.getDocumentRow = function() { + var docRow = 0; + var rowCache = this.session.$screenRowCache; + if (rowCache.length) { + var index = this.session.$getRowCacheIndex(rowCache, this.currentRow); + if (index >= 0) + docRow = this.session.$docRowCache[index]; + } + return docRow; + }; + BidiHandler.prototype.getSplitIndex = function() { + var splitIndex = 0; + var rowCache = this.session.$screenRowCache; + if (rowCache.length) { + var currentIndex, prevIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow); + while (this.currentRow - splitIndex > 0) { + currentIndex = this.session.$getRowCacheIndex(rowCache, this.currentRow - splitIndex - 1); + if (currentIndex !== prevIndex) + break; + prevIndex = currentIndex; + splitIndex++; + } + } else { + splitIndex = this.currentRow; + } + return splitIndex; + }; + BidiHandler.prototype.updateRowLine = function(docRow, splitIndex) { + if (docRow === undefined) + docRow = this.getDocumentRow(); + var isLastRow = (docRow === this.session.getLength() - 1), + endOfLine = isLastRow ? this.EOF : this.EOL; + this.wrapIndent = 0; + this.line = this.session.getLine(docRow); + this.isRtlDir = this.$isRtl || this.line.charAt(0) === this.RLE; + if (this.session.$useWrapMode) { + var splits = this.session.$wrapData[docRow]; + if (splits) { + if (splitIndex === undefined) + splitIndex = this.getSplitIndex(); + if (splitIndex > 0 && splits.length) { + this.wrapIndent = splits.indent; + this.wrapOffset = this.wrapIndent * this.charWidths[bidiUtil.L]; + this.line = (splitIndex < splits.length) ? + this.line.substring(splits[splitIndex - 1], splits[splitIndex]) : + this.line.substring(splits[splits.length - 1]); + } else { + this.line = this.line.substring(0, splits[splitIndex]); + } + if (splitIndex == splits.length) { + this.line += (this.showInvisibles) ? endOfLine : bidiUtil.DOT; + } + } + } else { + this.line += this.showInvisibles ? endOfLine : bidiUtil.DOT; + } + var session = this.session, + shift = 0, + size; + this.line = this.line.replace(/\t|[\u1100-\u2029, \u202F-\uFFE6]/g, function(ch, i) { + if (ch === '\t' || session.isFullWidth(ch.charCodeAt(0))) { + size = (ch === '\t') ? session.getScreenTabSize(i + shift) : 2; + shift += size - 1; + return lang.stringRepeat(bidiUtil.DOT, size); + } + return ch; + }); + if (this.isRtlDir) { + this.fontMetrics.$main.textContent = (this.line.charAt(this.line.length - 1) == bidiUtil.DOT) ? this.line.substr(0, this.line.length - 1) : this.line; + this.rtlLineOffset = this.contentWidth - this.fontMetrics.$main.getBoundingClientRect().width; + } + }; + BidiHandler.prototype.updateBidiMap = function() { + var textCharTypes = []; + if (bidiUtil.hasBidiCharacters(this.line, textCharTypes) || this.isRtlDir) { + this.bidiMap = bidiUtil.doBidiReorder(this.line, textCharTypes, this.isRtlDir); + } else { + this.bidiMap = {}; + } + }; + BidiHandler.prototype.markAsDirty = function() { + this.currentRow = null; + }; + BidiHandler.prototype.updateCharacterWidths = function(fontMetrics) { + if (this.characterWidth === fontMetrics.$characterSize.width) + return; + this.fontMetrics = fontMetrics; + var characterWidth = this.characterWidth = fontMetrics.$characterSize.width; + var bidiCharWidth = fontMetrics.$measureCharWidth("\u05d4"); + this.charWidths[bidiUtil.L] = this.charWidths[bidiUtil.EN] = this.charWidths[bidiUtil.ON_R] = characterWidth; + this.charWidths[bidiUtil.R] = this.charWidths[bidiUtil.AN] = bidiCharWidth; + this.charWidths[bidiUtil.R_H] = bidiCharWidth * 0.45; + this.charWidths[bidiUtil.B] = this.charWidths[bidiUtil.RLE] = 0; + this.currentRow = null; + }; + BidiHandler.prototype.setShowInvisibles = function(showInvisibles) { + this.showInvisibles = showInvisibles; + this.currentRow = null; + }; + BidiHandler.prototype.setEolChar = function(eolChar) { + this.EOL = eolChar; + }; + BidiHandler.prototype.setContentWidth = function(width) { + this.contentWidth = width; + }; + BidiHandler.prototype.isRtlLine = function(row) { + if (this.$isRtl) + return true; + if (row != undefined) + return (this.session.getLine(row).charAt(0) == this.RLE); + else + return this.isRtlDir; + }; + BidiHandler.prototype.setRtlDirection = function(editor, isRtlDir) { + var cursor = editor.getCursorPosition(); + for (var row = editor.selection.getSelectionAnchor().row; row <= cursor.row; row++) { + if (!isRtlDir && editor.session.getLine(row).charAt(0) === editor.session.$bidiHandler.RLE) + editor.session.doc.removeInLine(row, 0, 1); + else if (isRtlDir && editor.session.getLine(row).charAt(0) !== editor.session.$bidiHandler.RLE) + editor.session.doc.insert({ + column: 0, + row: row + }, editor.session.$bidiHandler.RLE); + } + }; + BidiHandler.prototype.getPosLeft = function(col) { + col -= this.wrapIndent; + var leftBoundary = (this.line.charAt(0) === this.RLE) ? 1 : 0; + var logicalIdx = (col > leftBoundary) ? (this.session.getOverwrite() ? col : col - 1) : leftBoundary; + var visualIdx = bidiUtil.getVisualFromLogicalIdx(logicalIdx, this.bidiMap), + levels = this.bidiMap.bidiLevels, + left = 0; + if (!this.session.getOverwrite() && col <= leftBoundary && levels[visualIdx] % 2 !== 0) + visualIdx++; + for (var i = 0; i < visualIdx; i++) { + left += this.charWidths[levels[i]]; + } + if (!this.session.getOverwrite() && (col > leftBoundary) && (levels[visualIdx] % 2 === 0)) + left += this.charWidths[levels[visualIdx]]; + if (this.wrapIndent) + left += this.isRtlDir ? (-1 * this.wrapOffset) : this.wrapOffset; + if (this.isRtlDir) + left += this.rtlLineOffset; + return left; + }; + BidiHandler.prototype.getSelections = function(startCol, endCol) { + var map = this.bidiMap, + levels = map.bidiLevels, + level, selections = [], + offset = 0, + selColMin = Math.min(startCol, endCol) - this.wrapIndent, + selColMax = Math.max(startCol, endCol) - this.wrapIndent, + isSelected = false, + isSelectedPrev = false, + selectionStart = 0; + if (this.wrapIndent) + offset += this.isRtlDir ? (-1 * this.wrapOffset) : this.wrapOffset; + for (var logIdx, visIdx = 0; visIdx < levels.length; visIdx++) { + logIdx = map.logicalFromVisual[visIdx]; + level = levels[visIdx]; + isSelected = (logIdx >= selColMin) && (logIdx < selColMax); + if (isSelected && !isSelectedPrev) { + selectionStart = offset; + } else if (!isSelected && isSelectedPrev) { + selections.push({ + left: selectionStart, + width: offset - selectionStart + }); + } + offset += this.charWidths[level]; + isSelectedPrev = isSelected; + } + if (isSelected && (visIdx === levels.length)) { + selections.push({ + left: selectionStart, + width: offset - selectionStart + }); + } + if (this.isRtlDir) { + for (var i = 0; i < selections.length; i++) { + selections[i].left += this.rtlLineOffset; + } + } + return selections; + }; + BidiHandler.prototype.offsetToCol = function(posX) { + if (this.isRtlDir) + posX -= this.rtlLineOffset; + var logicalIdx = 0, + posX = Math.max(posX, 0), + offset = 0, + visualIdx = 0, + levels = this.bidiMap.bidiLevels, + charWidth = this.charWidths[levels[visualIdx]]; + if (this.wrapIndent) + posX -= this.isRtlDir ? (-1 * this.wrapOffset) : this.wrapOffset; + while (posX > offset + charWidth / 2) { + offset += charWidth; + if (visualIdx === levels.length - 1) { + charWidth = 0; + break; + } + charWidth = this.charWidths[levels[++visualIdx]]; + } + if (visualIdx > 0 && (levels[visualIdx - 1] % 2 !== 0) && (levels[visualIdx] % 2 === 0)) { + if (posX < offset) + visualIdx--; + logicalIdx = this.bidiMap.logicalFromVisual[visualIdx]; + } else if (visualIdx > 0 && (levels[visualIdx - 1] % 2 === 0) && (levels[visualIdx] % 2 !== 0)) { + logicalIdx = 1 + ((posX > offset) ? this.bidiMap.logicalFromVisual[visualIdx] : + this.bidiMap.logicalFromVisual[visualIdx - 1]); + } else if ((this.isRtlDir && visualIdx === levels.length - 1 && charWidth === 0 && (levels[visualIdx - 1] % 2 === 0)) || + (!this.isRtlDir && visualIdx === 0 && (levels[visualIdx] % 2 !== 0))) { + logicalIdx = 1 + this.bidiMap.logicalFromVisual[visualIdx]; + } else { + if (visualIdx > 0 && (levels[visualIdx - 1] % 2 !== 0) && charWidth !== 0) + visualIdx--; + logicalIdx = this.bidiMap.logicalFromVisual[visualIdx]; + } + if (logicalIdx === 0 && this.isRtlDir) + logicalIdx++; + return (logicalIdx + this.wrapIndent); + }; + return BidiHandler; + }()); + exports.BidiHandler = BidiHandler; + +}); + +define("ace/selection", ["require", "exports", "module", "ace/lib/oop", "ace/lib/lang", "ace/lib/event_emitter", "ace/range"], function(require, exports, module) { + "use strict"; + var oop = require("./lib/oop"); + var lang = require("./lib/lang"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var Range = require("./range").Range; + var Selection = /** @class */ (function() { + function Selection(session) { + this.session = session; + this.doc = session.getDocument(); + this.clearSelection(); + this.cursor = this.lead = this.doc.createAnchor(0, 0); + this.anchor = this.doc.createAnchor(0, 0); + this.$silent = false; + var self = this; + this.cursor.on("change", function(e) { + self.$cursorChanged = true; + if (!self.$silent) + self._emit("changeCursor"); + if (!self.$isEmpty && !self.$silent) + self._emit("changeSelection"); + if (!self.$keepDesiredColumnOnChange && e.old.column != e.value.column) + self.$desiredColumn = null; + }); + this.anchor.on("change", function() { + self.$anchorChanged = true; + if (!self.$isEmpty && !self.$silent) + self._emit("changeSelection"); + }); + } + Selection.prototype.isEmpty = function() { + return this.$isEmpty || (this.anchor.row == this.lead.row && + this.anchor.column == this.lead.column); + }; + Selection.prototype.isMultiLine = function() { + return !this.$isEmpty && this.anchor.row != this.cursor.row; + }; + Selection.prototype.getCursor = function() { + return this.lead.getPosition(); + }; + Selection.prototype.setAnchor = function(row, column) { + this.$isEmpty = false; + this.anchor.setPosition(row, column); + }; + Selection.prototype.getAnchor = function() { + if (this.$isEmpty) + return this.getSelectionLead(); + return this.anchor.getPosition(); + }; + Selection.prototype.getSelectionLead = function() { + return this.lead.getPosition(); + }; + Selection.prototype.isBackwards = function() { + var anchor = this.anchor; + var lead = this.lead; + return (anchor.row > lead.row || (anchor.row == lead.row && anchor.column > lead.column)); + }; + Selection.prototype.getRange = function() { + var anchor = this.anchor; + var lead = this.lead; + if (this.$isEmpty) + return Range.fromPoints(lead, lead); + return this.isBackwards() ? + Range.fromPoints(lead, anchor) : + Range.fromPoints(anchor, lead); + }; + Selection.prototype.clearSelection = function() { + if (!this.$isEmpty) { + this.$isEmpty = true; + this._emit("changeSelection"); + } + }; + Selection.prototype.selectAll = function() { + this.$setSelection(0, 0, Number.MAX_VALUE, Number.MAX_VALUE); + }; + Selection.prototype.setRange = function(range, reverse) { + var start = reverse ? range.end : range.start; + var end = reverse ? range.start : range.end; + this.$setSelection(start.row, start.column, end.row, end.column); + }; + Selection.prototype.$setSelection = function(anchorRow, anchorColumn, cursorRow, cursorColumn) { + if (this.$silent) + return; + var wasEmpty = this.$isEmpty; + var wasMultiselect = this.inMultiSelectMode; + this.$silent = true; + this.$cursorChanged = this.$anchorChanged = false; + this.anchor.setPosition(anchorRow, anchorColumn); + this.cursor.setPosition(cursorRow, cursorColumn); + this.$isEmpty = !Range.comparePoints(this.anchor, this.cursor); + this.$silent = false; + if (this.$cursorChanged) + this._emit("changeCursor"); + if (this.$cursorChanged || this.$anchorChanged || wasEmpty != this.$isEmpty || wasMultiselect) + this._emit("changeSelection"); + }; + Selection.prototype.$moveSelection = function(mover) { + var lead = this.lead; + if (this.$isEmpty) + this.setSelectionAnchor(lead.row, lead.column); + mover.call(this); + }; + Selection.prototype.selectTo = function(row, column) { + this.$moveSelection(function() { + this.moveCursorTo(row, column); + }); + }; + Selection.prototype.selectToPosition = function(pos) { + this.$moveSelection(function() { + this.moveCursorToPosition(pos); + }); + }; + Selection.prototype.moveTo = function(row, column) { + this.clearSelection(); + this.moveCursorTo(row, column); + }; + Selection.prototype.moveToPosition = function(pos) { + this.clearSelection(); + this.moveCursorToPosition(pos); + }; + Selection.prototype.selectUp = function() { + this.$moveSelection(this.moveCursorUp); + }; + Selection.prototype.selectDown = function() { + this.$moveSelection(this.moveCursorDown); + }; + Selection.prototype.selectRight = function() { + this.$moveSelection(this.moveCursorRight); + }; + Selection.prototype.selectLeft = function() { + this.$moveSelection(this.moveCursorLeft); + }; + Selection.prototype.selectLineStart = function() { + this.$moveSelection(this.moveCursorLineStart); + }; + Selection.prototype.selectLineEnd = function() { + this.$moveSelection(this.moveCursorLineEnd); + }; + Selection.prototype.selectFileEnd = function() { + this.$moveSelection(this.moveCursorFileEnd); + }; + Selection.prototype.selectFileStart = function() { + this.$moveSelection(this.moveCursorFileStart); + }; + Selection.prototype.selectWordRight = function() { + this.$moveSelection(this.moveCursorWordRight); + }; + Selection.prototype.selectWordLeft = function() { + this.$moveSelection(this.moveCursorWordLeft); + }; + Selection.prototype.getWordRange = function(row, column) { + if (typeof column == "undefined") { + var cursor = row || this.lead; + row = cursor.row; + column = cursor.column; + } + return this.session.getWordRange(row, column); + }; + Selection.prototype.selectWord = function() { + this.setSelectionRange(this.getWordRange()); + }; + Selection.prototype.selectAWord = function() { + var cursor = this.getCursor(); + var range = this.session.getAWordRange(cursor.row, cursor.column); + this.setSelectionRange(range); + }; + Selection.prototype.getLineRange = function(row, excludeLastChar) { + var rowStart = typeof row == "number" ? row : this.lead.row; + var rowEnd; + var foldLine = this.session.getFoldLine(rowStart); + if (foldLine) { + rowStart = foldLine.start.row; + rowEnd = foldLine.end.row; + } else { + rowEnd = rowStart; + } + if (excludeLastChar === true) + return new Range(rowStart, 0, rowEnd, this.session.getLine(rowEnd).length); + else + return new Range(rowStart, 0, rowEnd + 1, 0); + }; + Selection.prototype.selectLine = function() { + this.setSelectionRange(this.getLineRange()); + }; + Selection.prototype.moveCursorUp = function() { + this.moveCursorBy(-1, 0); + }; + Selection.prototype.moveCursorDown = function() { + this.moveCursorBy(1, 0); + }; + Selection.prototype.wouldMoveIntoSoftTab = function(cursor, tabSize, direction) { + var start = cursor.column; + var end = cursor.column + tabSize; + if (direction < 0) { + start = cursor.column - tabSize; + end = cursor.column; + } + return this.session.isTabStop(cursor) && this.doc.getLine(cursor.row).slice(start, end).split(" ").length - 1 == tabSize; + }; + Selection.prototype.moveCursorLeft = function() { + var cursor = this.lead.getPosition(), + fold; + if (fold = this.session.getFoldAt(cursor.row, cursor.column, -1)) { + this.moveCursorTo(fold.start.row, fold.start.column); + } else if (cursor.column === 0) { + if (cursor.row > 0) { + this.moveCursorTo(cursor.row - 1, this.doc.getLine(cursor.row - 1).length); + } + } else { + var tabSize = this.session.getTabSize(); + if (this.wouldMoveIntoSoftTab(cursor, tabSize, -1) && !this.session.getNavigateWithinSoftTabs()) { + this.moveCursorBy(0, -tabSize); + } else { + this.moveCursorBy(0, -1); + } + } + }; + Selection.prototype.moveCursorRight = function() { + var cursor = this.lead.getPosition(), + fold; + if (fold = this.session.getFoldAt(cursor.row, cursor.column, 1)) { + this.moveCursorTo(fold.end.row, fold.end.column); + } else if (this.lead.column == this.doc.getLine(this.lead.row).length) { + if (this.lead.row < this.doc.getLength() - 1) { + this.moveCursorTo(this.lead.row + 1, 0); + } + } else { + var tabSize = this.session.getTabSize(); + var cursor = this.lead; + if (this.wouldMoveIntoSoftTab(cursor, tabSize, 1) && !this.session.getNavigateWithinSoftTabs()) { + this.moveCursorBy(0, tabSize); + } else { + this.moveCursorBy(0, 1); + } + } + }; + Selection.prototype.moveCursorLineStart = function() { + var row = this.lead.row; + var column = this.lead.column; + var screenRow = this.session.documentToScreenRow(row, column); + var firstColumnPosition = this.session.screenToDocumentPosition(screenRow, 0); + var beforeCursor = this.session.getDisplayLine(row, null, firstColumnPosition.row, firstColumnPosition.column); + var leadingSpace = beforeCursor.match(/^\s*/); + if (leadingSpace[0].length != column && !this.session.$useEmacsStyleLineStart) + firstColumnPosition.column += leadingSpace[0].length; + this.moveCursorToPosition(firstColumnPosition); + }; + Selection.prototype.moveCursorLineEnd = function() { + var lead = this.lead; + var lineEnd = this.session.getDocumentLastRowColumnPosition(lead.row, lead.column); + if (this.lead.column == lineEnd.column) { + var line = this.session.getLine(lineEnd.row); + if (lineEnd.column == line.length) { + var textEnd = line.search(/\s+$/); + if (textEnd > 0) + lineEnd.column = textEnd; + } + } + this.moveCursorTo(lineEnd.row, lineEnd.column); + }; + Selection.prototype.moveCursorFileEnd = function() { + var row = this.doc.getLength() - 1; + var column = this.doc.getLine(row).length; + this.moveCursorTo(row, column); + }; + Selection.prototype.moveCursorFileStart = function() { + this.moveCursorTo(0, 0); + }; + Selection.prototype.moveCursorLongWordRight = function() { + var row = this.lead.row; + var column = this.lead.column; + var line = this.doc.getLine(row); + var rightOfCursor = line.substring(column); + this.session.nonTokenRe.lastIndex = 0; + this.session.tokenRe.lastIndex = 0; + var fold = this.session.getFoldAt(row, column, 1); + if (fold) { + this.moveCursorTo(fold.end.row, fold.end.column); + return; + } + if (this.session.nonTokenRe.exec(rightOfCursor)) { + column += this.session.nonTokenRe.lastIndex; + this.session.nonTokenRe.lastIndex = 0; + rightOfCursor = line.substring(column); + } + if (column >= line.length) { + this.moveCursorTo(row, line.length); + this.moveCursorRight(); + if (row < this.doc.getLength() - 1) + this.moveCursorWordRight(); + return; + } + if (this.session.tokenRe.exec(rightOfCursor)) { + column += this.session.tokenRe.lastIndex; + this.session.tokenRe.lastIndex = 0; + } + this.moveCursorTo(row, column); + }; + Selection.prototype.moveCursorLongWordLeft = function() { + var row = this.lead.row; + var column = this.lead.column; + var fold; + if (fold = this.session.getFoldAt(row, column, -1)) { + this.moveCursorTo(fold.start.row, fold.start.column); + return; + } + var str = this.session.getFoldStringAt(row, column, -1); + if (str == null) { + str = this.doc.getLine(row).substring(0, column); + } + var leftOfCursor = lang.stringReverse(str); + this.session.nonTokenRe.lastIndex = 0; + this.session.tokenRe.lastIndex = 0; + if (this.session.nonTokenRe.exec(leftOfCursor)) { + column -= this.session.nonTokenRe.lastIndex; + leftOfCursor = leftOfCursor.slice(this.session.nonTokenRe.lastIndex); + this.session.nonTokenRe.lastIndex = 0; + } + if (column <= 0) { + this.moveCursorTo(row, 0); + this.moveCursorLeft(); + if (row > 0) + this.moveCursorWordLeft(); + return; + } + if (this.session.tokenRe.exec(leftOfCursor)) { + column -= this.session.tokenRe.lastIndex; + this.session.tokenRe.lastIndex = 0; + } + this.moveCursorTo(row, column); + }; + Selection.prototype.$shortWordEndIndex = function(rightOfCursor) { + var index = 0, + ch; + var whitespaceRe = /\s/; + var tokenRe = this.session.tokenRe; + tokenRe.lastIndex = 0; + if (this.session.tokenRe.exec(rightOfCursor)) { + index = this.session.tokenRe.lastIndex; + } else { + while ((ch = rightOfCursor[index]) && whitespaceRe.test(ch)) + index++; + if (index < 1) { + tokenRe.lastIndex = 0; + while ((ch = rightOfCursor[index]) && !tokenRe.test(ch)) { + tokenRe.lastIndex = 0; + index++; + if (whitespaceRe.test(ch)) { + if (index > 2) { + index--; + break; + } else { + while ((ch = rightOfCursor[index]) && whitespaceRe.test(ch)) + index++; + if (index > 2) + break; + } + } + } + } + } + tokenRe.lastIndex = 0; + return index; + }; + Selection.prototype.moveCursorShortWordRight = function() { + var row = this.lead.row; + var column = this.lead.column; + var line = this.doc.getLine(row); + var rightOfCursor = line.substring(column); + var fold = this.session.getFoldAt(row, column, 1); + if (fold) + return this.moveCursorTo(fold.end.row, fold.end.column); + if (column == line.length) { + var l = this.doc.getLength(); + do { + row++; + rightOfCursor = this.doc.getLine(row); + } while (row < l && /^\s*$/.test(rightOfCursor)); + if (!/^\s+/.test(rightOfCursor)) + rightOfCursor = ""; + column = 0; + } + var index = this.$shortWordEndIndex(rightOfCursor); + this.moveCursorTo(row, column + index); + }; + Selection.prototype.moveCursorShortWordLeft = function() { + var row = this.lead.row; + var column = this.lead.column; + var fold; + if (fold = this.session.getFoldAt(row, column, -1)) + return this.moveCursorTo(fold.start.row, fold.start.column); + var line = this.session.getLine(row).substring(0, column); + if (column === 0) { + do { + row--; + line = this.doc.getLine(row); + } while (row > 0 && /^\s*$/.test(line)); + column = line.length; + if (!/\s+$/.test(line)) + line = ""; + } + var leftOfCursor = lang.stringReverse(line); + var index = this.$shortWordEndIndex(leftOfCursor); + return this.moveCursorTo(row, column - index); + }; + Selection.prototype.moveCursorWordRight = function() { + if (this.session.$selectLongWords) + this.moveCursorLongWordRight(); + else + this.moveCursorShortWordRight(); + }; + Selection.prototype.moveCursorWordLeft = function() { + if (this.session.$selectLongWords) + this.moveCursorLongWordLeft(); + else + this.moveCursorShortWordLeft(); + }; + Selection.prototype.moveCursorBy = function(rows, chars) { + var screenPos = this.session.documentToScreenPosition(this.lead.row, this.lead.column); + var offsetX; + if (chars === 0) { + if (rows !== 0) { + if (this.session.$bidiHandler.isBidiRow(screenPos.row, this.lead.row)) { + offsetX = this.session.$bidiHandler.getPosLeft(screenPos.column); + screenPos.column = Math.round(offsetX / this.session.$bidiHandler.charWidths[0]); + } else { + offsetX = screenPos.column * this.session.$bidiHandler.charWidths[0]; + } + } + if (this.$desiredColumn) + screenPos.column = this.$desiredColumn; + else + this.$desiredColumn = screenPos.column; + } + if (rows != 0 && this.session.lineWidgets && this.session.lineWidgets[this.lead.row]) { + var widget = this.session.lineWidgets[this.lead.row]; + if (rows < 0) + rows -= widget.rowsAbove || 0; + else if (rows > 0) + rows += widget.rowCount - (widget.rowsAbove || 0); + } + var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenPos.column, offsetX); + if (rows !== 0 && chars === 0 && docPos.row === this.lead.row && docPos.column === this.lead.column) {} + this.moveCursorTo(docPos.row, docPos.column + chars, chars === 0); + }; + Selection.prototype.moveCursorToPosition = function(position) { + this.moveCursorTo(position.row, position.column); + }; + Selection.prototype.moveCursorTo = function(row, column, keepDesiredColumn) { + var fold = this.session.getFoldAt(row, column, 1); + if (fold) { + row = fold.start.row; + column = fold.start.column; + } + this.$keepDesiredColumnOnChange = true; + var line = this.session.getLine(row); + if (/[\uDC00-\uDFFF]/.test(line.charAt(column)) && line.charAt(column - 1)) { + if (this.lead.row == row && this.lead.column == column + 1) + column = column - 1; + else + column = column + 1; + } + this.lead.setPosition(row, column); + this.$keepDesiredColumnOnChange = false; + if (!keepDesiredColumn) + this.$desiredColumn = null; + }; + Selection.prototype.moveCursorToScreen = function(row, column, keepDesiredColumn) { + var pos = this.session.screenToDocumentPosition(row, column); + this.moveCursorTo(pos.row, pos.column, keepDesiredColumn); + }; + Selection.prototype.detach = function() { + this.lead.detach(); + this.anchor.detach(); + }; + Selection.prototype.fromOrientedRange = function(range) { + this.setSelectionRange(range, range.cursor == range.start); + this.$desiredColumn = range.desiredColumn || this.$desiredColumn; + }; + Selection.prototype.toOrientedRange = function(range) { + var r = this.getRange(); + if (range) { + range.start.column = r.start.column; + range.start.row = r.start.row; + range.end.column = r.end.column; + range.end.row = r.end.row; + } else { + range = r; + } + range.cursor = this.isBackwards() ? range.start : range.end; + range.desiredColumn = this.$desiredColumn; + return range; + }; + Selection.prototype.getRangeOfMovements = function(func) { + var start = this.getCursor(); + try { + func(this); + var end = this.getCursor(); + return Range.fromPoints(start, end); + } catch (e) { + return Range.fromPoints(start, start); + } finally { + this.moveCursorToPosition(start); + } + }; + Selection.prototype.toJSON = function() { + if (this.rangeCount) { + var data = this.ranges.map(function(r) { + var r1 = r.clone(); + r1.isBackwards = r.cursor == r.start; + return r1; + }); + } else { + var data = this.getRange(); + data.isBackwards = this.isBackwards(); + } + return data; + }; + Selection.prototype.fromJSON = function(data) { + if (data.start == undefined) { + if (this.rangeList && data.length > 1) { + this.toSingleRange(data[0]); + for (var i = data.length; i--;) { + var r = Range.fromPoints(data[i].start, data[i].end); + if (data[i].isBackwards) + r.cursor = r.start; + this.addRange(r, true); + } + return; + } else { + data = data[0]; + } + } + if (this.rangeList) + this.toSingleRange(data); + this.setSelectionRange(data, data.isBackwards); + }; + Selection.prototype.isEqual = function(data) { + if ((data.length || this.rangeCount) && data.length != this.rangeCount) + return false; + if (!data.length || !this.ranges) + return this.getRange().isEqual(data); + for (var i = this.ranges.length; i--;) { + if (!this.ranges[i].isEqual(data[i])) + return false; + } + return true; + }; + return Selection; + }()); + Selection.prototype.setSelectionAnchor = Selection.prototype.setAnchor; + Selection.prototype.getSelectionAnchor = Selection.prototype.getAnchor; + Selection.prototype.setSelectionRange = Selection.prototype.setRange; + oop.implement(Selection.prototype, EventEmitter); + exports.Selection = Selection; + +}); + +define("ace/tokenizer", ["require", "exports", "module", "ace/lib/report_error"], function(require, exports, module) { + "use strict"; + var reportError = require("./lib/report_error").reportError; + var MAX_TOKEN_COUNT = 2000; + var Tokenizer = /** @class */ (function() { + function Tokenizer(rules) { + this.splitRegex; + this.states = rules; + this.regExps = {}; + this.matchMappings = {}; + for (var key in this.states) { + var state = this.states[key]; + var ruleRegExps = []; + var matchTotal = 0; + var mapping = this.matchMappings[key] = { + defaultToken: "text" + }; + var flag = "g"; + var splitterRurles = []; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + if (rule.defaultToken) + mapping.defaultToken = rule.defaultToken; + if (rule.caseInsensitive && flag.indexOf("i") === -1) + flag += "i"; + if (rule.unicode && flag.indexOf("u") === -1) + flag += "u"; + if (rule.regex == null) + continue; + if (rule.regex instanceof RegExp) + rule.regex = rule.regex.toString().slice(1, -1); + var adjustedregex = rule.regex; + var matchcount = new RegExp("(?:(" + adjustedregex + ")|(.))").exec("a").length - 2; + if (Array.isArray(rule.token)) { + if (rule.token.length == 1 || matchcount == 1) { + rule.token = rule.token[0]; + } else if (matchcount - 1 != rule.token.length) { + this.reportError("number of classes and regexp groups doesn't match", { + rule: rule, + groupCount: matchcount - 1 + }); + rule.token = rule.token[0]; + } else { + rule.tokenArray = rule.token; + rule.token = null; + rule.onMatch = this.$arrayTokens; + } + } else if (typeof rule.token == "function" && !rule.onMatch) { + if (matchcount > 1) + rule.onMatch = this.$applyToken; + else + rule.onMatch = rule.token; + } + if (matchcount > 1) { + if (/\\\d/.test(rule.regex)) { + adjustedregex = rule.regex.replace(/\\([0-9]+)/g, function(match, digit) { + return "\\" + (parseInt(digit, 10) + matchTotal + 1); + }); + } else { + matchcount = 1; + adjustedregex = this.removeCapturingGroups(rule.regex); + } + if (!rule.splitRegex && typeof rule.token != "string") + splitterRurles.push(rule); // flag will be known only at the very end + } + mapping[matchTotal] = i; + matchTotal += matchcount; + ruleRegExps.push(adjustedregex); + if (!rule.onMatch) + rule.onMatch = null; + } + if (!ruleRegExps.length) { + mapping[0] = 0; + ruleRegExps.push("$"); + } + splitterRurles.forEach(function(rule) { + rule.splitRegex = this.createSplitterRegexp(rule.regex, flag); + }, this); + this.regExps[key] = new RegExp("(" + ruleRegExps.join(")|(") + ")|($)", flag); + } + } + Tokenizer.prototype.$setMaxTokenCount = function(m) { + MAX_TOKEN_COUNT = m | 0; + }; + Tokenizer.prototype.$applyToken = function(str) { + var values = this.splitRegex.exec(str).slice(1); + var types = this.token.apply(this, values); + if (typeof types === "string") + return [{ + type: types, + value: str + }]; + var tokens = []; + for (var i = 0, l = types.length; i < l; i++) { + if (values[i]) + tokens[tokens.length] = { + type: types[i], + value: values[i] + }; + } + return tokens; + }; + Tokenizer.prototype.$arrayTokens = function(str) { + if (!str) + return []; + var values = this.splitRegex.exec(str); + if (!values) + return "text"; + var tokens = []; + var types = this.tokenArray; + for (var i = 0, l = types.length; i < l; i++) { + if (values[i + 1]) + tokens[tokens.length] = { + type: types[i], + value: values[i + 1] + }; + } + return tokens; + }; + Tokenizer.prototype.removeCapturingGroups = function(src) { + var r = src.replace(/\\.|\[(?:\\.|[^\\\]])*|\(\?[:=!<]|(\()/g, function(x, y) { + return y ? "(?:" : x; + }); + return r; + }; + Tokenizer.prototype.createSplitterRegexp = function(src, flag) { + if (src.indexOf("(?=") != -1) { + var stack = 0; + var inChClass = false; + var lastCapture = {}; + src.replace(/(\\.)|(\((?:\?[=!])?)|(\))|([\[\]])/g, function(m, esc, parenOpen, parenClose, square, index) { + if (inChClass) { + inChClass = square != "]"; + } else if (square) { + inChClass = true; + } else if (parenClose) { + if (stack == lastCapture.stack) { + lastCapture.end = index + 1; + lastCapture.stack = -1; + } + stack--; + } else if (parenOpen) { + stack++; + if (parenOpen.length != 1) { + lastCapture.stack = stack; + lastCapture.start = index; + } + } + return m; + }); + if (lastCapture.end != null && /^\)*$/.test(src.substr(lastCapture.end))) + src = src.substring(0, lastCapture.start) + src.substr(lastCapture.end); + } + if (src.charAt(0) != "^") + src = "^" + src; + if (src.charAt(src.length - 1) != "$") + src += "$"; + return new RegExp(src, (flag || "").replace("g", "")); + }; + Tokenizer.prototype.getLineTokens = function(line, startState) { + if (startState && typeof startState != "string") { + var stack = startState.slice(0); + startState = stack[0]; + if (startState === "#tmp") { + stack.shift(); + startState = stack.shift(); + } + } else + var stack = []; + var currentState = /**@type{string}*/ (startState) || "start"; + var state = this.states[currentState]; + if (!state) { + currentState = "start"; + state = this.states[currentState]; + } + var mapping = this.matchMappings[currentState]; + var re = this.regExps[currentState]; + re.lastIndex = 0; + var match, tokens = []; + var lastIndex = 0; + var matchAttempts = 0; + var token = { + type: null, + value: "" + }; + while (match = re.exec(line)) { + var type = mapping.defaultToken; + var rule = null; + var value = match[0]; + var index = re.lastIndex; + if (index - value.length > lastIndex) { + var skipped = line.substring(lastIndex, index - value.length); + if (token.type == type) { + token.value += skipped; + } else { + if (token.type) + tokens.push(token); + token = { + type: type, + value: skipped + }; + } + } + for (var i = 0; i < match.length - 2; i++) { + if (match[i + 1] === undefined) + continue; + rule = state[mapping[i]]; + if (rule.onMatch) + type = rule.onMatch(value, currentState, stack, line); + else + type = rule.token; + if (rule.next) { + if (typeof rule.next == "string") { + currentState = rule.next; + } else { + currentState = rule.next(currentState, stack); + } + state = this.states[currentState]; + if (!state) { + this.reportError("state doesn't exist", currentState); + currentState = "start"; + state = this.states[currentState]; + } + mapping = this.matchMappings[currentState]; + lastIndex = index; + re = this.regExps[currentState]; + re.lastIndex = index; + } + if (rule.consumeLineEnd) + lastIndex = index; + break; + } + if (value) { + if (typeof type === "string") { + if ((!rule || rule.merge !== false) && token.type === type) { + token.value += value; + } else { + if (token.type) + tokens.push(token); + token = { + type: type, + value: value + }; + } + } else if (type) { + if (token.type) + tokens.push(token); + token = { + type: null, + value: "" + }; + for (var i = 0; i < type.length; i++) + tokens.push(type[i]); + } + } + if (lastIndex == line.length) + break; + lastIndex = index; + if (matchAttempts++ > MAX_TOKEN_COUNT) { + if (matchAttempts > 2 * line.length) { + this.reportError("infinite loop with in ace tokenizer", { + startState: startState, + line: line + }); + } + while (lastIndex < line.length) { + if (token.type) + tokens.push(token); + token = { + value: line.substring(lastIndex, lastIndex += 500), + type: "overflow" + }; + } + currentState = "start"; + stack = []; + break; + } + } + if (token.type) + tokens.push(token); + if (stack.length > 1) { + if (stack[0] !== currentState) + stack.unshift("#tmp", currentState); + } + return { + tokens: tokens, + state: stack.length ? stack : currentState + }; + }; + return Tokenizer; + }()); + Tokenizer.prototype.reportError = reportError; + exports.Tokenizer = Tokenizer; + +}); + +define("ace/mode/text_highlight_rules", ["require", "exports", "module", "ace/lib/deep_copy"], function(require, exports, module) { + "use strict"; + var deepCopy = require("../lib/deep_copy").deepCopy; + var TextHighlightRules; + TextHighlightRules = function() { + this.$rules = { + "start": [{ + token: "empty_line", + regex: '^$' + }, { + defaultToken: "text" + }] + }; + }; + (function() { + this.addRules = function(rules, prefix) { + if (!prefix) { + for (var key in rules) + this.$rules[key] = rules[key]; + return; + } + for (var key in rules) { + var state = rules[key]; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + if (rule.next || rule.onMatch) { + if (typeof rule.next == "string") { + if (rule.next.indexOf(prefix) !== 0) + rule.next = prefix + rule.next; + } + if (rule.nextState && rule.nextState.indexOf(prefix) !== 0) + rule.nextState = prefix + rule.nextState; + } + } + this.$rules[prefix + key] = state; + } + }; + this.getRules = function() { + return this.$rules; + }; + this.embedRules = function(HighlightRules, prefix, escapeRules, states, append) { + var embedRules = typeof HighlightRules == "function" ? + new HighlightRules().getRules() : + HighlightRules; + if (states) { + for (var i = 0; i < states.length; i++) + states[i] = prefix + states[i]; + } else { + states = []; + for (var key in embedRules) + states.push(prefix + key); + } + this.addRules(embedRules, prefix); + if (escapeRules) { + var addRules = Array.prototype[append ? "push" : "unshift"]; + for (var i = 0; i < states.length; i++) + addRules.apply(this.$rules[states[i]], deepCopy(escapeRules)); + } + if (!this.$embeds) + this.$embeds = []; + this.$embeds.push(prefix); + }; + this.getEmbeds = function() { + return this.$embeds; + }; + var pushState = function(currentState, stack) { + if (currentState != "start" || stack.length) + stack.unshift(this.nextState, currentState); + return this.nextState; + }; + var popState = function(currentState, stack) { + stack.shift(); + return stack.shift() || "start"; + }; + this.normalizeRules = function() { + var id = 0; + var rules = this.$rules; + + function processState(key) { + var state = rules[key]; + state["processed"] = true; + for (var i = 0; i < state.length; i++) { + var rule = state[i]; + var toInsert = null; + if (Array.isArray(rule)) { + toInsert = rule; + rule = {}; + } + if (!rule.regex && rule.start) { + rule.regex = rule.start; + if (!rule.next) + rule.next = []; + rule.next.push({ + defaultToken: rule.token + }, { + token: rule.token + ".end", + regex: rule.end || rule.start, + next: "pop" + }); + rule.token = rule.token + ".start"; + rule.push = true; + } + var next = rule.next || rule.push; + if (next && Array.isArray(next)) { + var stateName = rule.stateName; + if (!stateName) { + stateName = rule.token; + if (typeof stateName != "string") + stateName = stateName[0] || ""; + if (rules[stateName]) + stateName += id++; + } + rules[stateName] = next; + rule.next = stateName; + processState(stateName); + } else if (next == "pop") { + rule.next = popState; + } + if (rule.push) { + rule.nextState = rule.next || rule.push; + rule.next = pushState; + delete rule.push; + } + if (rule.rules) { + for (var r in rule.rules) { + if (rules[r]) { + if (rules[r].push) + rules[r].push.apply(rules[r], rule.rules[r]); + } else { + rules[r] = rule.rules[r]; + } + } + } + var includeName = typeof rule == "string" ? rule : rule.include; + if (includeName) { + if (includeName === "$self") + includeName = "start"; + if (Array.isArray(includeName)) + toInsert = includeName.map(function(x) { + return rules[x]; + }); + else + toInsert = rules[includeName]; + } + if (toInsert) { + var args = [i, 1].concat(toInsert); + if (rule.noEscape) + args = args.filter(function(x) { + return !x.next; + }); + state.splice.apply(state, args); + i--; + } + if (rule.keywordMap) { + rule.token = this.createKeywordMapper(rule.keywordMap, rule.defaultToken || "text", rule.caseInsensitive); + delete rule.defaultToken; + } + } + } + Object.keys(rules).forEach(processState, this); + }; + this.createKeywordMapper = function(map, defaultToken, ignoreCase, splitChar) { + var keywords = Object.create(null); + this.$keywordList = []; + Object.keys(map).forEach(function(className) { + var a = map[className]; + var list = a.split(splitChar || "|"); + for (var i = list.length; i--;) { + var word = list[i]; + this.$keywordList.push(word); + if (ignoreCase) + word = word.toLowerCase(); + keywords[word] = className; + } + }, this); + map = null; + return ignoreCase ? + function(value) { + return keywords[value.toLowerCase()] || defaultToken; + } : + function(value) { + return keywords[value] || defaultToken; + }; + }; + this.getKeywords = function() { + return this.$keywords; + }; + }).call(TextHighlightRules.prototype); + exports.TextHighlightRules = TextHighlightRules; + +}); + +define("ace/mode/behaviour", ["require", "exports", "module"], function(require, exports, module) { + "use strict"; + var Behaviour; + Behaviour = function() { + this.$behaviours = {}; + }; + (function() { + this.add = function(name, action, callback) { + switch (undefined) { + case this.$behaviours: + this.$behaviours = {}; + case this.$behaviours[name]: + this.$behaviours[name] = {}; + } + this.$behaviours[name][action] = callback; + }; + this.addBehaviours = function(behaviours) { + for (var key in behaviours) { + for (var action in behaviours[key]) { + this.add(key, action, behaviours[key][action]); + } + } + }; + this.remove = function(name) { + if (this.$behaviours && this.$behaviours[name]) { + delete this.$behaviours[name]; + } + }; + this.inherit = function(mode, filter) { + if (typeof mode === "function") { + var behaviours = new mode().getBehaviours(filter); + } else { + var behaviours = mode.getBehaviours(filter); + } + this.addBehaviours(behaviours); + }; + this.getBehaviours = function(filter) { + if (!filter) { + return this.$behaviours; + } else { + var ret = {}; + for (var i = 0; i < filter.length; i++) { + if (this.$behaviours[filter[i]]) { + ret[filter[i]] = this.$behaviours[filter[i]]; + } + } + return ret; + } + }; + }).call(Behaviour.prototype); + exports.Behaviour = Behaviour; + +}); + +define("ace/token_iterator", ["require", "exports", "module", "ace/range"], function(require, exports, module) { + "use strict"; + var Range = require("./range").Range; + var TokenIterator = /** @class */ (function() { + function TokenIterator(session, initialRow, initialColumn) { + this.$session = session; + this.$row = initialRow; + this.$rowTokens = session.getTokens(initialRow); + var token = session.getTokenAt(initialRow, initialColumn); + this.$tokenIndex = token ? token.index : -1; + } + TokenIterator.prototype.stepBackward = function() { + this.$tokenIndex -= 1; + while (this.$tokenIndex < 0) { + this.$row -= 1; + if (this.$row < 0) { + this.$row = 0; + return null; + } + this.$rowTokens = this.$session.getTokens(this.$row); + this.$tokenIndex = this.$rowTokens.length - 1; + } + return this.$rowTokens[this.$tokenIndex]; + }; + TokenIterator.prototype.stepForward = function() { + this.$tokenIndex += 1; + var rowCount; + while (this.$tokenIndex >= this.$rowTokens.length) { + this.$row += 1; + if (!rowCount) + rowCount = this.$session.getLength(); + if (this.$row >= rowCount) { + this.$row = rowCount - 1; + return null; + } + this.$rowTokens = this.$session.getTokens(this.$row); + this.$tokenIndex = 0; + } + return this.$rowTokens[this.$tokenIndex]; + }; + TokenIterator.prototype.getCurrentToken = function() { + return this.$rowTokens[this.$tokenIndex]; + }; + TokenIterator.prototype.getCurrentTokenRow = function() { + return this.$row; + }; + TokenIterator.prototype.getCurrentTokenColumn = function() { + var rowTokens = this.$rowTokens; + var tokenIndex = this.$tokenIndex; + var column = rowTokens[tokenIndex].start; + if (column !== undefined) + return column; + column = 0; + while (tokenIndex > 0) { + tokenIndex -= 1; + column += rowTokens[tokenIndex].value.length; + } + return column; + }; + TokenIterator.prototype.getCurrentTokenPosition = function() { + return { + row: this.$row, + column: this.getCurrentTokenColumn() + }; + }; + TokenIterator.prototype.getCurrentTokenRange = function() { + var token = this.$rowTokens[this.$tokenIndex]; + var column = this.getCurrentTokenColumn(); + return new Range(this.$row, column, this.$row, column + token.value.length); + }; + return TokenIterator; + }()); + exports.TokenIterator = TokenIterator; + +}); + +define("ace/mode/behaviour/cstyle", ["require", "exports", "module", "ace/lib/oop", "ace/mode/behaviour", "ace/token_iterator", "ace/lib/lang"], function(require, exports, module) { + "use strict"; + var oop = require("../../lib/oop"); + var Behaviour = require("../behaviour").Behaviour; + var TokenIterator = require("../../token_iterator").TokenIterator; + var lang = require("../../lib/lang"); + var SAFE_INSERT_IN_TOKENS = ["text", "paren.rparen", "rparen", "paren", "punctuation.operator"]; + var SAFE_INSERT_BEFORE_TOKENS = ["text", "paren.rparen", "rparen", "paren", "punctuation.operator", "comment"]; + var context; + var contextCache = {}; + var defaultQuotes = { + '"': '"', + "'": "'" + }; + var initContext = function(editor) { + var id = -1; + if (editor.multiSelect) { + id = editor.selection.index; + if (contextCache.rangeCount != editor.multiSelect.rangeCount) + contextCache = { + rangeCount: editor.multiSelect.rangeCount + }; + } + if (contextCache[id]) + return context = contextCache[id]; + context = contextCache[id] = { + autoInsertedBrackets: 0, + autoInsertedRow: -1, + autoInsertedLineEnd: "", + maybeInsertedBrackets: 0, + maybeInsertedRow: -1, + maybeInsertedLineStart: "", + maybeInsertedLineEnd: "" + }; + }; + var getWrapped = function(selection, selected, opening, closing) { + var rowDiff = selection.end.row - selection.start.row; + return { + text: opening + selected + closing, + selection: [ + 0, + selection.start.column + 1, + rowDiff, + selection.end.column + (rowDiff ? 0 : 1) + ] + }; + }; + var CstyleBehaviour; + CstyleBehaviour = function(options) { + options = options || {}; + this.add("braces", "insertion", function(state, action, editor, session, text) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (text == '{') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + var token = session.getTokenAt(cursor.row, cursor.column); + if (selected !== "" && selected !== "{" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '{', '}'); + } else if (token && /(?:string)\.quasi|\.xml/.test(token.type)) { + var excludeTokens = [ + /tag\-(?:open|name)/, /attribute\-name/ + ]; + if (excludeTokens.some(function(el) { + return el.test(token.type); + }) || /(string)\.quasi/.test(token.type) && + token.value[cursor.column - token.start - 1] !== '$') + return; + CstyleBehaviour.recordAutoInsert(editor, session, "}"); + return { + text: '{}', + selection: [1, 1] + }; + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + if (/[\]\}\)]/.test(line[cursor.column]) || editor.inMultiSelectMode || options.braces) { + CstyleBehaviour.recordAutoInsert(editor, session, "}"); + return { + text: '{}', + selection: [1, 1] + }; + } else { + CstyleBehaviour.recordMaybeInsert(editor, session, "{"); + return { + text: '{', + selection: [1, 1] + }; + } + } + } else if (text == '}') { + initContext(editor); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == '}') { + var matching = session.$findOpeningBracket('}', { + column: cursor.column + 1, + row: cursor.row + }); + if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } else if (text == "\n" || text == "\r\n") { + initContext(editor); + var closing = ""; + if (CstyleBehaviour.isMaybeInsertedClosing(cursor, line)) { + closing = lang.stringRepeat("}", context.maybeInsertedBrackets); + CstyleBehaviour.clearMaybeInsertedClosing(); + } + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar === '}') { + var openBracePos = session.findMatchingBracket({ + row: cursor.row, + column: cursor.column + 1 + }, '}'); + if (!openBracePos) + return null; + var next_indent = this.$getIndent(session.getLine(openBracePos.row)); + } else if (closing) { + var next_indent = this.$getIndent(line); + } else { + CstyleBehaviour.clearMaybeInsertedClosing(); + return; + } + var indent = next_indent + session.getTabString(); + return { + text: '\n' + indent + '\n' + next_indent + closing, + selection: [1, indent.length, 1, indent.length] + }; + } else { + CstyleBehaviour.clearMaybeInsertedClosing(); + } + }); + this.add("braces", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '{') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.end.column, range.end.column + 1); + if (rightChar == '}') { + range.end.column++; + return range; + } else { + context.maybeInsertedBrackets--; + } + } + }); + this.add("parens", "insertion", function(state, action, editor, session, text) { + if (text == '(') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '(', ')'); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + CstyleBehaviour.recordAutoInsert(editor, session, ")"); + return { + text: '()', + selection: [1, 1] + }; + } + } else if (text == ')') { + initContext(editor); + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == ')') { + var matching = session.$findOpeningBracket(')', { + column: cursor.column + 1, + row: cursor.row + }); + if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } + }); + this.add("parens", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '(') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == ')') { + range.end.column++; + return range; + } + } + }); + this.add("brackets", "insertion", function(state, action, editor, session, text) { + if (text == '[') { + initContext(editor); + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, '[', ']'); + } else if (CstyleBehaviour.isSaneInsertion(editor, session)) { + CstyleBehaviour.recordAutoInsert(editor, session, "]"); + return { + text: '[]', + selection: [1, 1] + }; + } + } else if (text == ']') { + initContext(editor); + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var rightChar = line.substring(cursor.column, cursor.column + 1); + if (rightChar == ']') { + var matching = session.$findOpeningBracket(']', { + column: cursor.column + 1, + row: cursor.row + }); + if (matching !== null && CstyleBehaviour.isAutoInsertedClosing(cursor, line, text)) { + CstyleBehaviour.popAutoInsertedClosing(); + return { + text: '', + selection: [1, 1] + }; + } + } + } + }); + this.add("brackets", "deletion", function(state, action, editor, session, range) { + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && selected == '[') { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == ']') { + range.end.column++; + return range; + } + } + }); + this.add("string_dquotes", "insertion", function(state, action, editor, session, text) { + var quotes = session.$mode.$quotes || defaultQuotes; + if (text.length == 1 && quotes[text]) { + if (this.lineCommentStart && this.lineCommentStart.indexOf(text) != -1) + return; + initContext(editor); + var quote = text; + var selection = editor.getSelectionRange(); + var selected = session.doc.getTextRange(selection); + if (selected !== "" && (selected.length != 1 || !quotes[selected]) && editor.getWrapBehavioursEnabled()) { + return getWrapped(selection, selected, quote, quote); + } else if (!selected) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + var leftChar = line.substring(cursor.column - 1, cursor.column); + var rightChar = line.substring(cursor.column, cursor.column + 1); + var token = session.getTokenAt(cursor.row, cursor.column); + var rightToken = session.getTokenAt(cursor.row, cursor.column + 1); + if (leftChar == "\\" && token && /escape/.test(token.type)) + return null; + var stringBefore = token && /string|escape/.test(token.type); + var stringAfter = !rightToken || /string|escape/.test(rightToken.type); + var pair; + if (rightChar == quote) { + pair = stringBefore !== stringAfter; + if (pair && /string\.end/.test(rightToken.type)) + pair = false; + } else { + if (stringBefore && !stringAfter) + return null; // wrap string with different quote + if (stringBefore && stringAfter) + return null; // do not pair quotes inside strings + var wordRe = session.$mode.tokenRe; + wordRe.lastIndex = 0; + var isWordBefore = wordRe.test(leftChar); + wordRe.lastIndex = 0; + var isWordAfter = wordRe.test(rightChar); + var pairQuotesAfter = session.$mode.$pairQuotesAfter; + var shouldPairQuotes = pairQuotesAfter && pairQuotesAfter[quote] && pairQuotesAfter[quote].test(leftChar); + if ((!shouldPairQuotes && isWordBefore) || isWordAfter) + return null; // before or after alphanumeric + if (rightChar && !/[\s;,.})\]\\]/.test(rightChar)) + return null; // there is rightChar and it isn't closing + var charBefore = line[cursor.column - 2]; + if (leftChar == quote && (charBefore == quote || wordRe.test(charBefore))) + return null; + pair = true; + } + return { + text: pair ? quote + quote : "", + selection: [1, 1] + }; + } + } + }); + this.add("string_dquotes", "deletion", function(state, action, editor, session, range) { + var quotes = session.$mode.$quotes || defaultQuotes; + var selected = session.doc.getTextRange(range); + if (!range.isMultiLine() && quotes.hasOwnProperty(selected)) { + initContext(editor); + var line = session.doc.getLine(range.start.row); + var rightChar = line.substring(range.start.column + 1, range.start.column + 2); + if (rightChar == selected) { + range.end.column++; + return range; + } + } + }); + if (options.closeDocComment !== false) { + this.add("doc comment end", "insertion", function(state, action, editor, session, text) { + if (state === "doc-start" && (text === "\n" || text === "\r\n") && editor.selection.isEmpty()) { + var cursor = editor.getCursorPosition(); + if (cursor.column === 0) { + return; + } + var line = session.doc.getLine(cursor.row); + var nextLine = session.doc.getLine(cursor.row + 1); + var tokens = session.getTokens(cursor.row); + var index = 0; + for (var i = 0; i < tokens.length; i++) { + index += tokens[i].value.length; + var currentToken = tokens[i]; + if (index >= cursor.column) { + if (index === cursor.column) { + if (!/\.doc/.test(currentToken.type)) { + return; + } + if (/\*\//.test(currentToken.value)) { + var nextToken = tokens[i + 1]; + if (!nextToken || !/\.doc/.test(nextToken.type)) { + return; + } + } + } + var cursorPosInToken = cursor.column - (index - currentToken.value.length); + var closeDocPos = currentToken.value.indexOf("*/"); + var openDocPos = currentToken.value.indexOf("/**", closeDocPos > -1 ? closeDocPos + 2 : 0); + if (openDocPos !== -1 && cursorPosInToken > openDocPos && cursorPosInToken < openDocPos + 3) { + return; + } + if (closeDocPos !== -1 && openDocPos !== -1 && cursorPosInToken >= closeDocPos && + cursorPosInToken <= openDocPos || !/\.doc/.test(currentToken.type)) { + return; + } + break; + } + } + var indent = this.$getIndent(line); + if (/\s*\*/.test(nextLine)) { + if (/^\s*\*/.test(line)) { + return { + text: text + indent + "* ", + selection: [1, 2 + indent.length, 1, 2 + indent.length] + }; + } else { + return { + text: text + indent + " * ", + selection: [1, 3 + indent.length, 1, 3 + indent.length] + }; + } + } + if (/\/\*\*/.test(line.substring(0, cursor.column))) { + return { + text: text + indent + " * " + text + " " + indent + "*/", + selection: [1, 4 + indent.length, 1, 4 + indent.length] + }; + } + } + }); + } + }; + CstyleBehaviour.isSaneInsertion = function(editor, session) { + var cursor = editor.getCursorPosition(); + var iterator = new TokenIterator(session, cursor.row, cursor.column); + if (!this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) { + if (/[)}\]]/.test(editor.session.getLine(cursor.row)[cursor.column])) + return true; + var iterator2 = new TokenIterator(session, cursor.row, cursor.column + 1); + if (!this.$matchTokenType(iterator2.getCurrentToken() || "text", SAFE_INSERT_IN_TOKENS)) + return false; + } + iterator.stepForward(); + return iterator.getCurrentTokenRow() !== cursor.row || + this.$matchTokenType(iterator.getCurrentToken() || "text", SAFE_INSERT_BEFORE_TOKENS); + }; + CstyleBehaviour["$matchTokenType"] = function(token, types) { + return types.indexOf(token.type || token) > -1; + }; + CstyleBehaviour["recordAutoInsert"] = function(editor, session, bracket) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (!this["isAutoInsertedClosing"](cursor, line, context.autoInsertedLineEnd[0])) + context.autoInsertedBrackets = 0; + context.autoInsertedRow = cursor.row; + context.autoInsertedLineEnd = bracket + line.substr(cursor.column); + context.autoInsertedBrackets++; + }; + CstyleBehaviour["recordMaybeInsert"] = function(editor, session, bracket) { + var cursor = editor.getCursorPosition(); + var line = session.doc.getLine(cursor.row); + if (!this["isMaybeInsertedClosing"](cursor, line)) + context.maybeInsertedBrackets = 0; + context.maybeInsertedRow = cursor.row; + context.maybeInsertedLineStart = line.substr(0, cursor.column) + bracket; + context.maybeInsertedLineEnd = line.substr(cursor.column); + context.maybeInsertedBrackets++; + }; + CstyleBehaviour["isAutoInsertedClosing"] = function(cursor, line, bracket) { + return context.autoInsertedBrackets > 0 && + cursor.row === context.autoInsertedRow && + bracket === context.autoInsertedLineEnd[0] && + line.substr(cursor.column) === context.autoInsertedLineEnd; + }; + CstyleBehaviour["isMaybeInsertedClosing"] = function(cursor, line) { + return context.maybeInsertedBrackets > 0 && + cursor.row === context.maybeInsertedRow && + line.substr(cursor.column) === context.maybeInsertedLineEnd && + line.substr(0, cursor.column) == context.maybeInsertedLineStart; + }; + CstyleBehaviour["popAutoInsertedClosing"] = function() { + context.autoInsertedLineEnd = context.autoInsertedLineEnd.substr(1); + context.autoInsertedBrackets--; + }; + CstyleBehaviour["clearMaybeInsertedClosing"] = function() { + if (context) { + context.maybeInsertedBrackets = 0; + context.maybeInsertedRow = -1; + } + }; + oop.inherits(CstyleBehaviour, Behaviour); + exports.CstyleBehaviour = CstyleBehaviour; + +}); + +define("ace/unicode", ["require", "exports", "module"], function(require, exports, module) { + "use strict"; + var wordChars = [48, 9, 8, 25, 5, 0, 2, 25, 48, 0, 11, 0, 5, 0, 6, 22, 2, 30, 2, 457, 5, 11, 15, 4, 8, 0, 2, 0, 18, 116, 2, 1, 3, 3, 9, 0, 2, 2, 2, 0, 2, 19, 2, 82, 2, 138, 2, 4, 3, 155, 12, 37, 3, 0, 8, 38, 10, 44, 2, 0, 2, 1, 2, 1, 2, 0, 9, 26, 6, 2, 30, 10, 7, 61, 2, 9, 5, 101, 2, 7, 3, 9, 2, 18, 3, 0, 17, 58, 3, 100, 15, 53, 5, 0, 6, 45, 211, 57, 3, 18, 2, 5, 3, 11, 3, 9, 2, 1, 7, 6, 2, 2, 2, 7, 3, 1, 3, 21, 2, 6, 2, 0, 4, 3, 3, 8, 3, 1, 3, 3, 9, 0, 5, 1, 2, 4, 3, 11, 16, 2, 2, 5, 5, 1, 3, 21, 2, 6, 2, 1, 2, 1, 2, 1, 3, 0, 2, 4, 5, 1, 3, 2, 4, 0, 8, 3, 2, 0, 8, 15, 12, 2, 2, 8, 2, 2, 2, 21, 2, 6, 2, 1, 2, 4, 3, 9, 2, 2, 2, 2, 3, 0, 16, 3, 3, 9, 18, 2, 2, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 3, 8, 3, 1, 3, 2, 9, 1, 5, 1, 2, 4, 3, 9, 2, 0, 17, 1, 2, 5, 4, 2, 2, 3, 4, 1, 2, 0, 2, 1, 4, 1, 4, 2, 4, 11, 5, 4, 4, 2, 2, 3, 3, 0, 7, 0, 15, 9, 18, 2, 2, 7, 2, 2, 2, 22, 2, 9, 2, 4, 4, 7, 2, 2, 2, 3, 8, 1, 2, 1, 7, 3, 3, 9, 19, 1, 2, 7, 2, 2, 2, 22, 2, 9, 2, 4, 3, 8, 2, 2, 2, 3, 8, 1, 8, 0, 2, 3, 3, 9, 19, 1, 2, 7, 2, 2, 2, 22, 2, 15, 4, 7, 2, 2, 2, 3, 10, 0, 9, 3, 3, 9, 11, 5, 3, 1, 2, 17, 4, 23, 2, 8, 2, 0, 3, 6, 4, 0, 5, 5, 2, 0, 2, 7, 19, 1, 14, 57, 6, 14, 2, 9, 40, 1, 2, 0, 3, 1, 2, 0, 3, 0, 7, 3, 2, 6, 2, 2, 2, 0, 2, 0, 3, 1, 2, 12, 2, 2, 3, 4, 2, 0, 2, 5, 3, 9, 3, 1, 35, 0, 24, 1, 7, 9, 12, 0, 2, 0, 2, 0, 5, 9, 2, 35, 5, 19, 2, 5, 5, 7, 2, 35, 10, 0, 58, 73, 7, 77, 3, 37, 11, 42, 2, 0, 4, 328, 2, 3, 3, 6, 2, 0, 2, 3, 3, 40, 2, 3, 3, 32, 2, 3, 3, 6, 2, 0, 2, 3, 3, 14, 2, 56, 2, 3, 3, 66, 5, 0, 33, 15, 17, 84, 13, 619, 3, 16, 2, 25, 6, 74, 22, 12, 2, 6, 12, 20, 12, 19, 13, 12, 2, 2, 2, 1, 13, 51, 3, 29, 4, 0, 5, 1, 3, 9, 34, 2, 3, 9, 7, 87, 9, 42, 6, 69, 11, 28, 4, 11, 5, 11, 11, 39, 3, 4, 12, 43, 5, 25, 7, 10, 38, 27, 5, 62, 2, 28, 3, 10, 7, 9, 14, 0, 89, 75, 5, 9, 18, 8, 13, 42, 4, 11, 71, 55, 9, 9, 4, 48, 83, 2, 2, 30, 14, 230, 23, 280, 3, 5, 3, 37, 3, 5, 3, 7, 2, 0, 2, 0, 2, 0, 2, 30, 3, 52, 2, 6, 2, 0, 4, 2, 2, 6, 4, 3, 3, 5, 5, 12, 6, 2, 2, 6, 67, 1, 20, 0, 29, 0, 14, 0, 17, 4, 60, 12, 5, 0, 4, 11, 18, 0, 5, 0, 3, 9, 2, 0, 4, 4, 7, 0, 2, 0, 2, 0, 2, 3, 2, 10, 3, 3, 6, 4, 5, 0, 53, 1, 2684, 46, 2, 46, 2, 132, 7, 6, 15, 37, 11, 53, 10, 0, 17, 22, 10, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 31, 48, 0, 470, 1, 36, 5, 2, 4, 6, 1, 5, 85, 3, 1, 3, 2, 2, 89, 2, 3, 6, 40, 4, 93, 18, 23, 57, 15, 513, 6581, 75, 20939, 53, 1164, 68, 45, 3, 268, 4, 27, 21, 31, 3, 13, 13, 1, 2, 24, 9, 69, 11, 1, 38, 8, 3, 102, 3, 1, 111, 44, 25, 51, 13, 68, 12, 9, 7, 23, 4, 0, 5, 45, 3, 35, 13, 28, 4, 64, 15, 10, 39, 54, 10, 13, 3, 9, 7, 22, 4, 1, 5, 66, 25, 2, 227, 42, 2, 1, 3, 9, 7, 11171, 13, 22, 5, 48, 8453, 301, 3, 61, 3, 105, 39, 6, 13, 4, 6, 11, 2, 12, 2, 4, 2, 0, 2, 1, 2, 1, 2, 107, 34, 362, 19, 63, 3, 53, 41, 11, 5, 15, 17, 6, 13, 1, 25, 2, 33, 4, 2, 134, 20, 9, 8, 25, 5, 0, 2, 25, 12, 88, 4, 5, 3, 5, 3, 5, 3, 2]; + var code = 0; + var str = []; + for (var i = 0; i < wordChars.length; i += 2) { + str.push(code += wordChars[i]); + if (wordChars[i + 1]) + str.push(45, code += wordChars[i + 1]); + } + exports.wordChars = String.fromCharCode.apply(null, str); + +}); + +define("ace/mode/text", ["require", "exports", "module", "ace/config", "ace/tokenizer", "ace/mode/text_highlight_rules", "ace/mode/behaviour/cstyle", "ace/unicode", "ace/lib/lang", "ace/token_iterator", "ace/range"], function(require, exports, module) { + "use strict"; + var config = require("../config"); + var Tokenizer = require("../tokenizer").Tokenizer; + var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; + var CstyleBehaviour = require("./behaviour/cstyle").CstyleBehaviour; + var unicode = require("../unicode"); + var lang = require("../lib/lang"); + var TokenIterator = require("../token_iterator").TokenIterator; + var Range = require("../range").Range; + var Mode; + Mode = function() { + this.HighlightRules = TextHighlightRules; + }; + (function() { + this.$defaultBehaviour = new CstyleBehaviour(); + this.tokenRe = new RegExp("^[" + unicode.wordChars + "\\$_]+", "g"); + this.nonTokenRe = new RegExp("^(?:[^" + unicode.wordChars + "\\$_]|\\s])+", "g"); + this.getTokenizer = function() { + if (!this.$tokenizer) { + this.$highlightRules = this.$highlightRules || new this.HighlightRules(this.$highlightRuleConfig); + this.$tokenizer = new Tokenizer(this.$highlightRules.getRules()); + } + return this.$tokenizer; + }; + this.lineCommentStart = ""; + this.blockComment = ""; + this.toggleCommentLines = function(state, session, startRow, endRow) { + var doc = session.doc; + var ignoreBlankLines = true; + var shouldRemove = true; + var minIndent = Infinity; + var tabSize = session.getTabSize(); + var insertAtTabStop = false; + if (!this.lineCommentStart) { + if (!this.blockComment) + return false; + var lineCommentStart = this.blockComment.start; + var lineCommentEnd = this.blockComment.end; + var regexpStart = new RegExp("^(\\s*)(?:" + lang.escapeRegExp(lineCommentStart) + ")"); + var regexpEnd = new RegExp("(?:" + lang.escapeRegExp(lineCommentEnd) + ")\\s*$"); + var comment = function(line, i) { + if (testRemove(line, i)) + return; + if (!ignoreBlankLines || /\S/.test(line)) { + doc.insertInLine({ + row: i, + column: line.length + }, lineCommentEnd); + doc.insertInLine({ + row: i, + column: minIndent + }, lineCommentStart); + } + }; + var uncomment = function(line, i) { + var m; + if (m = line.match(regexpEnd)) + doc.removeInLine(i, line.length - m[0].length, line.length); + if (m = line.match(regexpStart)) + doc.removeInLine(i, m[1].length, m[0].length); + }; + var testRemove = function(line, row) { + if (regexpStart.test(line)) + return true; + var tokens = session.getTokens(row); + for (var i = 0; i < tokens.length; i++) { + if (tokens[i].type === "comment") + return true; + } + }; + } else { + if (Array.isArray(this.lineCommentStart)) { + var regexpStart = this.lineCommentStart.map(lang.escapeRegExp).join("|"); + var lineCommentStart = this.lineCommentStart[0]; + } else { + var regexpStart = lang.escapeRegExp(this.lineCommentStart); + var lineCommentStart = this.lineCommentStart; + } + regexpStart = new RegExp("^(\\s*)(?:" + regexpStart + ") ?"); + insertAtTabStop = session.getUseSoftTabs(); + var uncomment = function(line, i) { + var m = line.match(regexpStart); + if (!m) + return; + var start = m[1].length, + end = m[0].length; + if (!shouldInsertSpace(line, start, end) && m[0][end - 1] == " ") + end--; + doc.removeInLine(i, start, end); + }; + var commentWithSpace = lineCommentStart + " "; + var comment = function(line, i) { + if (!ignoreBlankLines || /\S/.test(line)) { + if (shouldInsertSpace(line, minIndent, minIndent)) + doc.insertInLine({ + row: i, + column: minIndent + }, commentWithSpace); + else + doc.insertInLine({ + row: i, + column: minIndent + }, lineCommentStart); + } + }; + var testRemove = function(line, i) { + return regexpStart.test(line); + }; + var shouldInsertSpace = function(line, before, after) { + var spaces = 0; + while (before-- && line.charAt(before) == " ") + spaces++; + if (spaces % tabSize != 0) + return false; + var spaces = 0; + while (line.charAt(after++) == " ") + spaces++; + if (tabSize > 2) + return spaces % tabSize != tabSize - 1; + else + return spaces % tabSize == 0; + }; + } + + function iter(fun) { + for (var i = startRow; i <= endRow; i++) + fun(doc.getLine(i), i); + } + var minEmptyLength = Infinity; + iter(function(line, i) { + var indent = line.search(/\S/); + if (indent !== -1) { + if (indent < minIndent) + minIndent = indent; + if (shouldRemove && !testRemove(line, i)) + shouldRemove = false; + } else if (minEmptyLength > line.length) { + minEmptyLength = line.length; + } + }); + if (minIndent == Infinity) { + minIndent = minEmptyLength; + ignoreBlankLines = false; + shouldRemove = false; + } + if (insertAtTabStop && minIndent % tabSize != 0) + minIndent = Math.floor(minIndent / tabSize) * tabSize; + iter(shouldRemove ? uncomment : comment); + }; + this.toggleBlockComment = function(state, session, range, cursor) { + var comment = this.blockComment; + if (!comment) + return; + if (!comment.start && comment[0]) + comment = comment[0]; + var iterator = new TokenIterator(session, cursor.row, cursor.column); + var token = iterator.getCurrentToken(); + var sel = session.selection; + var initialRange = session.selection.toOrientedRange(); + var startRow, colDiff; + if (token && /comment/.test(token.type)) { + var startRange, endRange; + while (token && /comment/.test(token.type)) { + var i = token.value.indexOf(comment.start); + if (i != -1) { + var row = iterator.getCurrentTokenRow(); + var column = iterator.getCurrentTokenColumn() + i; + startRange = new Range(row, column, row, column + comment.start.length); + break; + } + token = iterator.stepBackward(); + } + var iterator = new TokenIterator(session, cursor.row, cursor.column); + var token = iterator.getCurrentToken(); + while (token && /comment/.test(token.type)) { + var i = token.value.indexOf(comment.end); + if (i != -1) { + var row = iterator.getCurrentTokenRow(); + var column = iterator.getCurrentTokenColumn() + i; + endRange = new Range(row, column, row, column + comment.end.length); + break; + } + token = iterator.stepForward(); + } + if (endRange) + session.remove(endRange); + if (startRange) { + session.remove(startRange); + startRow = startRange.start.row; + colDiff = -comment.start.length; + } + } else { + colDiff = comment.start.length; + startRow = range.start.row; + session.insert(range.end, comment.end); + session.insert(range.start, comment.start); + } + if (initialRange.start.row == startRow) + initialRange.start.column += colDiff; + if (initialRange.end.row == startRow) + initialRange.end.column += colDiff; + session.selection.fromOrientedRange(initialRange); + }; + this.getNextLineIndent = function(state, line, tab) { + return this.$getIndent(line); + }; + this.checkOutdent = function(state, line, input) { + return false; + }; + this.autoOutdent = function(state, doc, row) {}; + this.$getIndent = function(line) { + return line.match(/^\s*/)[0]; + }; + this.createWorker = function(session) { + return null; + }; + this.createModeDelegates = function(mapping) { + this.$embeds = []; + this.$modes = {}; + for (var i in mapping) { + if (mapping[i]) { + var Mode = mapping[i]; + var id = Mode.prototype.$id; + var mode = config.$modes[id]; + if (!mode) + config.$modes[id] = mode = new Mode(); + if (!config.$modes[i]) + config.$modes[i] = mode; + this.$embeds.push(i); + this.$modes[i] = mode; + } + } + var delegations = ["toggleBlockComment", "toggleCommentLines", "getNextLineIndent", + "checkOutdent", "autoOutdent", "transformAction", "getCompletions" + ]; + var _loop_1 = function(i) { + (function(scope) { + var functionName = delegations[i]; + var defaultHandler = scope[functionName]; + scope[delegations[i]] = + function() { + return this.$delegator(functionName, arguments, defaultHandler); + }; + }(this_1)); + }; + var this_1 = this; + for (var i = 0; i < delegations.length; i++) { + _loop_1(i); + } + }; + this.$delegator = function(method, args, defaultHandler) { + var state = args[0] || "start"; + if (typeof state != "string") { + if (Array.isArray(state[2])) { + var language = state[2][state[2].length - 1]; + var mode = this.$modes[language]; + if (mode) + return mode[method].apply(mode, [state[1]].concat([].slice.call(args, 1))); + } + state = state[0] || "start"; + } + for (var i = 0; i < this.$embeds.length; i++) { + if (!this.$modes[this.$embeds[i]]) + continue; + var split = state.split(this.$embeds[i]); + if (!split[0] && split[1]) { + args[0] = split[1]; + var mode = this.$modes[this.$embeds[i]]; + return mode[method].apply(mode, args); + } + } + var ret = defaultHandler.apply(this, args); + return defaultHandler ? ret : undefined; + }; + this.transformAction = function(state, action, editor, session, param) { + if (this.$behaviour) { + var behaviours = this.$behaviour.getBehaviours(); + for (var key in behaviours) { + if (behaviours[key][action]) { + var ret = behaviours[key][action].apply(this, arguments); + if (ret) { + return ret; + } + } + } + } + }; + this.getKeywords = function(append) { + if (!this.completionKeywords) { + var rules = this.$tokenizer["rules"]; + var completionKeywords = []; + for (var rule in rules) { + var ruleItr = rules[rule]; + for (var r = 0, l = ruleItr.length; r < l; r++) { + if (typeof ruleItr[r].token === "string") { + if (/keyword|support|storage/.test(ruleItr[r].token)) + completionKeywords.push(ruleItr[r].regex); + } else if (typeof ruleItr[r].token === "object") { + for (var a = 0, aLength = ruleItr[r].token.length; a < aLength; a++) { + if (/keyword|support|storage/.test(ruleItr[r].token[a])) { + var rule = ruleItr[r].regex.match(/\(.+?\)/g)[a]; + completionKeywords.push(rule.substr(1, rule.length - 2)); + } + } + } + } + } + this.completionKeywords = completionKeywords; + } + if (!append) + return this.$keywordList; + return completionKeywords.concat(this.$keywordList || []); + }; + this.$createKeywordList = function() { + if (!this.$highlightRules) + this.getTokenizer(); + return this.$keywordList = this.$highlightRules.$keywordList || []; + }; + this.getCompletions = function(state, session, pos, prefix) { + var keywords = this.$keywordList || this.$createKeywordList(); + return keywords.map(function(word) { + return { + name: word, + value: word, + score: 0, + meta: "keyword" + }; + }); + }; + this.$id = "ace/mode/text"; + }).call(Mode.prototype); + exports.Mode = Mode; + +}); + +define("ace/apply_delta", ["require", "exports", "module"], function(require, exports, module) { + "use strict"; + + function throwDeltaError(delta, errorText) { + console.log("Invalid Delta:", delta); + throw "Invalid Delta: " + errorText; + } + + function positionInDocument(docLines, position) { + return position.row >= 0 && position.row < docLines.length && + position.column >= 0 && position.column <= docLines[position.row].length; + } + + function validateDelta(docLines, delta) { + if (delta.action != "insert" && delta.action != "remove") + throwDeltaError(delta, "delta.action must be 'insert' or 'remove'"); + if (!(delta.lines instanceof Array)) + throwDeltaError(delta, "delta.lines must be an Array"); + if (!delta.start || !delta.end) + throwDeltaError(delta, "delta.start/end must be an present"); + var start = delta.start; + if (!positionInDocument(docLines, delta.start)) + throwDeltaError(delta, "delta.start must be contained in document"); + var end = delta.end; + if (delta.action == "remove" && !positionInDocument(docLines, end)) + throwDeltaError(delta, "delta.end must contained in document for 'remove' actions"); + var numRangeRows = end.row - start.row; + var numRangeLastLineChars = (end.column - (numRangeRows == 0 ? start.column : 0)); + if (numRangeRows != delta.lines.length - 1 || delta.lines[numRangeRows].length != numRangeLastLineChars) + throwDeltaError(delta, "delta.range must match delta lines"); + } + exports.applyDelta = function(docLines, delta, doNotValidate) { + var row = delta.start.row; + var startColumn = delta.start.column; + var line = docLines[row] || ""; + switch (delta.action) { + case "insert": + var lines = delta.lines; + if (lines.length === 1) { + docLines[row] = line.substring(0, startColumn) + delta.lines[0] + line.substring(startColumn); + } else { + var args = [row, 1].concat(delta.lines); + docLines.splice.apply(docLines, args); + docLines[row] = line.substring(0, startColumn) + docLines[row]; + docLines[row + delta.lines.length - 1] += line.substring(startColumn); + } + break; + case "remove": + var endColumn = delta.end.column; + var endRow = delta.end.row; + if (row === endRow) { + docLines[row] = line.substring(0, startColumn) + line.substring(endColumn); + } else { + docLines.splice(row, endRow - row + 1, line.substring(0, startColumn) + docLines[endRow].substring(endColumn)); + } + break; + } + }; + +}); + +define("ace/anchor", ["require", "exports", "module", "ace/lib/oop", "ace/lib/event_emitter"], function(require, exports, module) { + "use strict"; + var oop = require("./lib/oop"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var Anchor = /** @class */ (function() { + function Anchor(doc, row, column) { + this.$onChange = this.onChange.bind(this); + this.attach(doc); + if (typeof row != "number") + this.setPosition(row.row, row.column); + else + this.setPosition(row, column); + } + Anchor.prototype.getPosition = function() { + return this.$clipPositionToDocument(this.row, this.column); + }; + Anchor.prototype.getDocument = function() { + return this.document; + }; + Anchor.prototype.onChange = function(delta) { + if (delta.start.row == delta.end.row && delta.start.row != this.row) + return; + if (delta.start.row > this.row) + return; + var point = $getTransformedPoint(delta, { + row: this.row, + column: this.column + }, this.$insertRight); + this.setPosition(point.row, point.column, true); + }; + Anchor.prototype.setPosition = function(row, column, noClip) { + var pos; + if (noClip) { + pos = { + row: row, + column: column + }; + } else { + pos = this.$clipPositionToDocument(row, column); + } + if (this.row == pos.row && this.column == pos.column) + return; + var old = { + row: this.row, + column: this.column + }; + this.row = pos.row; + this.column = pos.column; + this._signal("change", { + old: old, + value: pos + }); + }; + Anchor.prototype.detach = function() { + this.document.off("change", this.$onChange); + }; + Anchor.prototype.attach = function(doc) { + this.document = doc || this.document; + this.document.on("change", this.$onChange); + }; + Anchor.prototype.$clipPositionToDocument = function(row, column) { + var pos = {}; + if (row >= this.document.getLength()) { + pos.row = Math.max(0, this.document.getLength() - 1); + pos.column = this.document.getLine(pos.row).length; + } else if (row < 0) { + pos.row = 0; + pos.column = 0; + } else { + pos.row = row; + pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column)); + } + if (column < 0) + pos.column = 0; + return pos; + }; + return Anchor; + }()); + Anchor.prototype.$insertRight = false; + oop.implement(Anchor.prototype, EventEmitter); + + function $pointsInOrder(point1, point2, equalPointsInOrder) { + var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column; + return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter); + } + + function $getTransformedPoint(delta, point, moveIfEqual) { + var deltaIsInsert = delta.action == "insert"; + var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.end.row - delta.start.row); + var deltaColShift = (deltaIsInsert ? 1 : -1) * (delta.end.column - delta.start.column); + var deltaStart = delta.start; + var deltaEnd = deltaIsInsert ? deltaStart : delta.end; // Collapse insert range. + if ($pointsInOrder(point, deltaStart, moveIfEqual)) { + return { + row: point.row, + column: point.column + }; + } + if ($pointsInOrder(deltaEnd, point, !moveIfEqual)) { + return { + row: point.row + deltaRowShift, + column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0) + }; + } + return { + row: deltaStart.row, + column: deltaStart.column + }; + } + exports.Anchor = Anchor; + +}); + +define("ace/document", ["require", "exports", "module", "ace/lib/oop", "ace/apply_delta", "ace/lib/event_emitter", "ace/range", "ace/anchor"], function(require, exports, module) { + "use strict"; + var oop = require("./lib/oop"); + var applyDelta = require("./apply_delta").applyDelta; + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var Range = require("./range").Range; + var Anchor = require("./anchor").Anchor; + var Document = /** @class */ (function() { + function Document(textOrLines) { + this.$lines = [""]; + if (textOrLines.length === 0) { + this.$lines = [""]; + } else if (Array.isArray(textOrLines)) { + this.insertMergedLines({ + row: 0, + column: 0 + }, textOrLines); + } else { + this.insert({ + row: 0, + column: 0 + }, textOrLines); + } + } + Document.prototype.setValue = function(text) { + var len = this.getLength() - 1; + this.remove(new Range(0, 0, len, this.getLine(len).length)); + this.insert({ + row: 0, + column: 0 + }, text || ""); + }; + Document.prototype.getValue = function() { + return this.getAllLines().join(this.getNewLineCharacter()); + }; + Document.prototype.createAnchor = function(row, column) { + return new Anchor(this, row, column); + }; + Document.prototype.$detectNewLine = function(text) { + var match = text.match(/^.*?(\r\n|\r|\n)/m); + this.$autoNewLine = match ? match[1] : "\n"; + this._signal("changeNewLineMode"); + }; + Document.prototype.getNewLineCharacter = function() { + switch (this.$newLineMode) { + case "windows": + return "\r\n"; + case "unix": + return "\n"; + default: + return this.$autoNewLine || "\n"; + } + }; + Document.prototype.setNewLineMode = function(newLineMode) { + if (this.$newLineMode === newLineMode) + return; + this.$newLineMode = newLineMode; + this._signal("changeNewLineMode"); + }; + Document.prototype.getNewLineMode = function() { + return this.$newLineMode; + }; + Document.prototype.isNewLine = function(text) { + return (text == "\r\n" || text == "\r" || text == "\n"); + }; + Document.prototype.getLine = function(row) { + return this.$lines[row] || ""; + }; + Document.prototype.getLines = function(firstRow, lastRow) { + return this.$lines.slice(firstRow, lastRow + 1); + }; + Document.prototype.getAllLines = function() { + return this.getLines(0, this.getLength()); + }; + Document.prototype.getLength = function() { + return this.$lines.length; + }; + Document.prototype.getTextRange = function(range) { + return this.getLinesForRange(range).join(this.getNewLineCharacter()); + }; + Document.prototype.getLinesForRange = function(range) { + var lines; + if (range.start.row === range.end.row) { + lines = [this.getLine(range.start.row).substring(range.start.column, range.end.column)]; + } else { + lines = this.getLines(range.start.row, range.end.row); + lines[0] = (lines[0] || "").substring(range.start.column); + var l = lines.length - 1; + if (range.end.row - range.start.row == l) + lines[l] = lines[l].substring(0, range.end.column); + } + return lines; + }; + Document.prototype.insertLines = function(row, lines) { + console.warn("Use of document.insertLines is deprecated. Use the insertFullLines method instead."); + return this.insertFullLines(row, lines); + }; + Document.prototype.removeLines = function(firstRow, lastRow) { + console.warn("Use of document.removeLines is deprecated. Use the removeFullLines method instead."); + return this.removeFullLines(firstRow, lastRow); + }; + Document.prototype.insertNewLine = function(position) { + console.warn("Use of document.insertNewLine is deprecated. Use insertMergedLines(position, ['', '']) instead."); + return this.insertMergedLines(position, ["", ""]); + }; + Document.prototype.insert = function(position, text) { + if (this.getLength() <= 1) + this.$detectNewLine(text); + return this.insertMergedLines(position, this.$split(text)); + }; + Document.prototype.insertInLine = function(position, text) { + var start = this.clippedPos(position.row, position.column); + var end = this.pos(position.row, position.column + text.length); + this.applyDelta({ + start: start, + end: end, + action: "insert", + lines: [text] + }, true); + return this.clonePos(end); + }; + Document.prototype.clippedPos = function(row, column) { + var length = this.getLength(); + if (row === undefined) { + row = length; + } else if (row < 0) { + row = 0; + } else if (row >= length) { + row = length - 1; + column = undefined; + } + var line = this.getLine(row); + if (column == undefined) + column = line.length; + column = Math.min(Math.max(column, 0), line.length); + return { + row: row, + column: column + }; + }; + Document.prototype.clonePos = function(pos) { + return { + row: pos.row, + column: pos.column + }; + }; + Document.prototype.pos = function(row, column) { + return { + row: row, + column: column + }; + }; + Document.prototype.$clipPosition = function(position) { + var length = this.getLength(); + if (position.row >= length) { + position.row = Math.max(0, length - 1); + position.column = this.getLine(length - 1).length; + } else { + position.row = Math.max(0, position.row); + position.column = Math.min(Math.max(position.column, 0), this.getLine(position.row).length); + } + return position; + }; + Document.prototype.insertFullLines = function(row, lines) { + row = Math.min(Math.max(row, 0), this.getLength()); + var column = 0; + if (row < this.getLength()) { + lines = lines.concat([""]); + column = 0; + } else { + lines = [""].concat(lines); + row--; + column = this.$lines[row].length; + } + this.insertMergedLines({ + row: row, + column: column + }, lines); + }; + Document.prototype.insertMergedLines = function(position, lines) { + var start = this.clippedPos(position.row, position.column); + var end = { + row: start.row + lines.length - 1, + column: (lines.length == 1 ? start.column : 0) + lines[lines.length - 1].length + }; + this.applyDelta({ + start: start, + end: end, + action: "insert", + lines: lines + }); + return this.clonePos(end); + }; + Document.prototype.remove = function(range) { + var start = this.clippedPos(range.start.row, range.start.column); + var end = this.clippedPos(range.end.row, range.end.column); + this.applyDelta({ + start: start, + end: end, + action: "remove", + lines: this.getLinesForRange({ + start: start, + end: end + }) + }); + return this.clonePos(start); + }; + Document.prototype.removeInLine = function(row, startColumn, endColumn) { + var start = this.clippedPos(row, startColumn); + var end = this.clippedPos(row, endColumn); + this.applyDelta({ + start: start, + end: end, + action: "remove", + lines: this.getLinesForRange({ + start: start, + end: end + }) + }, true); + return this.clonePos(start); + }; + Document.prototype.removeFullLines = function(firstRow, lastRow) { + firstRow = Math.min(Math.max(0, firstRow), this.getLength() - 1); + lastRow = Math.min(Math.max(0, lastRow), this.getLength() - 1); + var deleteFirstNewLine = lastRow == this.getLength() - 1 && firstRow > 0; + var deleteLastNewLine = lastRow < this.getLength() - 1; + var startRow = (deleteFirstNewLine ? firstRow - 1 : firstRow); + var startCol = (deleteFirstNewLine ? this.getLine(startRow).length : 0); + var endRow = (deleteLastNewLine ? lastRow + 1 : lastRow); + var endCol = (deleteLastNewLine ? 0 : this.getLine(endRow).length); + var range = new Range(startRow, startCol, endRow, endCol); + var deletedLines = this.$lines.slice(firstRow, lastRow + 1); + this.applyDelta({ + start: range.start, + end: range.end, + action: "remove", + lines: this.getLinesForRange(range) + }); + return deletedLines; + }; + Document.prototype.removeNewLine = function(row) { + if (row < this.getLength() - 1 && row >= 0) { + this.applyDelta({ + start: this.pos(row, this.getLine(row).length), + end: this.pos(row + 1, 0), + action: "remove", + lines: ["", ""] + }); + } + }; + Document.prototype.replace = function(range, text) { + if (!(range instanceof Range)) + range = Range.fromPoints(range.start, range.end); + if (text.length === 0 && range.isEmpty()) + return range.start; + if (text == this.getTextRange(range)) + return range.end; + this.remove(range); + var end; + if (text) { + end = this.insert(range.start, text); + } else { + end = range.start; + } + return end; + }; + Document.prototype.applyDeltas = function(deltas) { + for (var i = 0; i < deltas.length; i++) { + this.applyDelta(deltas[i]); + } + }; + Document.prototype.revertDeltas = function(deltas) { + for (var i = deltas.length - 1; i >= 0; i--) { + this.revertDelta(deltas[i]); + } + }; + Document.prototype.applyDelta = function(delta, doNotValidate) { + var isInsert = delta.action == "insert"; + if (isInsert ? delta.lines.length <= 1 && !delta.lines[0] : + !Range.comparePoints(delta.start, delta.end)) { + return; + } + if (isInsert && delta.lines.length > 20000) { + this.$splitAndapplyLargeDelta(delta, 20000); + } else { + applyDelta(this.$lines, delta, doNotValidate); + this._signal("change", delta); + } + }; + Document.prototype.$safeApplyDelta = function(delta) { + var docLength = this.$lines.length; + if (delta.action == "remove" && delta.start.row < docLength && delta.end.row < docLength || + delta.action == "insert" && delta.start.row <= docLength) { + this.applyDelta(delta); + } + }; + Document.prototype.$splitAndapplyLargeDelta = function(delta, MAX) { + var lines = delta.lines; + var l = lines.length - MAX + 1; + var row = delta.start.row; + var column = delta.start.column; + for (var from = 0, to = 0; from < l; from = to) { + to += MAX - 1; + var chunk = lines.slice(from, to); + chunk.push(""); + this.applyDelta({ + start: this.pos(row + from, column), + end: this.pos(row + to, column = 0), + action: delta.action, + lines: chunk + }, true); + } + delta.lines = lines.slice(from); + delta.start.row = row + from; + delta.start.column = column; + this.applyDelta(delta, true); + }; + Document.prototype.revertDelta = function(delta) { + this.$safeApplyDelta({ + start: this.clonePos(delta.start), + end: this.clonePos(delta.end), + action: (delta.action == "insert" ? "remove" : "insert"), + lines: delta.lines.slice() + }); + }; + Document.prototype.indexToPosition = function(index, startRow) { + var lines = this.$lines || this.getAllLines(); + var newlineLength = this.getNewLineCharacter().length; + for (var i = startRow || 0, l = lines.length; i < l; i++) { + index -= lines[i].length + newlineLength; + if (index < 0) + return { + row: i, + column: index + lines[i].length + newlineLength + }; + } + return { + row: l - 1, + column: index + lines[l - 1].length + newlineLength + }; + }; + Document.prototype.positionToIndex = function(pos, startRow) { + var lines = this.$lines || this.getAllLines(); + var newlineLength = this.getNewLineCharacter().length; + var index = 0; + var row = Math.min(pos.row, lines.length); + for (var i = startRow || 0; i < row; ++i) + index += lines[i].length + newlineLength; + return index + pos.column; + }; + Document.prototype.$split = function(text) { + return text.split(/\r\n|\r|\n/); + }; + return Document; + }()); + Document.prototype.$autoNewLine = ""; + Document.prototype.$newLineMode = "auto"; + oop.implement(Document.prototype, EventEmitter); + exports.Document = Document; + +}); + +define("ace/background_tokenizer", ["require", "exports", "module", "ace/lib/oop", "ace/lib/event_emitter"], function(require, exports, module) { + "use strict"; + var oop = require("./lib/oop"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var BackgroundTokenizer = /** @class */ (function() { + function BackgroundTokenizer(tokenizer, session) { + this.running = false; + this.lines = []; + this.states = []; + this.currentLine = 0; + this.tokenizer = tokenizer; + var self = this; + this.$worker = function() { + if (!self.running) { + return; + } + var workerStart = new Date(); + var currentLine = self.currentLine; + var endLine = -1; + var doc = self.doc; + var startLine = currentLine; + while (self.lines[currentLine]) + currentLine++; + var len = doc.getLength(); + var processedLines = 0; + self.running = false; + while (currentLine < len) { + self.$tokenizeRow(currentLine); + endLine = currentLine; + do { + currentLine++; + } while (self.lines[currentLine]); + processedLines++; + if ((processedLines % 5 === 0) && (new Date() - workerStart) > 20) { + self.running = setTimeout(self.$worker, 20); + break; + } + } + self.currentLine = currentLine; + if (endLine == -1) + endLine = currentLine; + if (startLine <= endLine) + self.fireUpdateEvent(startLine, endLine); + }; + } + BackgroundTokenizer.prototype.setTokenizer = function(tokenizer) { + this.tokenizer = tokenizer; + this.lines = []; + this.states = []; + this.start(0); + }; + BackgroundTokenizer.prototype.setDocument = function(doc) { + this.doc = doc; + this.lines = []; + this.states = []; + this.stop(); + }; + BackgroundTokenizer.prototype.fireUpdateEvent = function(firstRow, lastRow) { + var data = { + first: firstRow, + last: lastRow + }; + this._signal("update", { + data: data + }); + }; + BackgroundTokenizer.prototype.start = function(startRow) { + this.currentLine = Math.min(startRow || 0, this.currentLine, this.doc.getLength()); + this.lines.splice(this.currentLine, this.lines.length); + this.states.splice(this.currentLine, this.states.length); + this.stop(); + this.running = setTimeout(this.$worker, 700); + }; + BackgroundTokenizer.prototype.scheduleStart = function() { + if (!this.running) + this.running = setTimeout(this.$worker, 700); + }; + BackgroundTokenizer.prototype.$updateOnChange = function(delta) { + var startRow = delta.start.row; + var len = delta.end.row - startRow; + if (len === 0) { + this.lines[startRow] = null; + } else if (delta.action == "remove") { + this.lines.splice(startRow, len + 1, null); + this.states.splice(startRow, len + 1, null); + } else { + var args = Array(len + 1); + args.unshift(startRow, 1); + this.lines.splice.apply(this.lines, args); + this.states.splice.apply(this.states, args); + } + this.currentLine = Math.min(startRow, this.currentLine, this.doc.getLength()); + this.stop(); + }; + BackgroundTokenizer.prototype.stop = function() { + if (this.running) + clearTimeout(this.running); + this.running = false; + }; + BackgroundTokenizer.prototype.getTokens = function(row) { + return this.lines[row] || this.$tokenizeRow(row); + }; + BackgroundTokenizer.prototype.getState = function(row) { + if (this.currentLine == row) + this.$tokenizeRow(row); + return this.states[row] || "start"; + }; + BackgroundTokenizer.prototype.$tokenizeRow = function(row) { + var line = this.doc.getLine(row); + var state = this.states[row - 1]; + var data = this.tokenizer.getLineTokens(line, state, row); + if (this.states[row] + "" !== data.state + "") { + this.states[row] = data.state; + this.lines[row + 1] = null; + if (this.currentLine > row + 1) + this.currentLine = row + 1; + } else if (this.currentLine == row) { + this.currentLine = row + 1; + } + return this.lines[row] = data.tokens; + }; + BackgroundTokenizer.prototype.cleanup = function() { + this.running = false; + this.lines = []; + this.states = []; + this.currentLine = 0; + this.removeAllListeners(); + }; + return BackgroundTokenizer; + }()); + oop.implement(BackgroundTokenizer.prototype, EventEmitter); + exports.BackgroundTokenizer = BackgroundTokenizer; + +}); + +define("ace/search_highlight", ["require", "exports", "module", "ace/lib/lang", "ace/range"], function(require, exports, module) { + "use strict"; + var lang = require("./lib/lang"); + var Range = require("./range").Range; + var SearchHighlight = /** @class */ (function() { + function SearchHighlight(regExp, clazz, type) { + if (type === void 0) { + type = "text"; + } + this.setRegexp(regExp); + this.clazz = clazz; + this.type = type; + } + SearchHighlight.prototype.setRegexp = function(regExp) { + if (this.regExp + "" == regExp + "") + return; + this.regExp = regExp; + this.cache = []; + }; + SearchHighlight.prototype.update = function(html, markerLayer, session, config) { + if (!this.regExp) + return; + var start = config.firstRow, + end = config.lastRow; + var renderedMarkerRanges = {}; + for (var i = start; i <= end; i++) { + var ranges = this.cache[i]; + if (ranges == null) { + ranges = lang.getMatchOffsets(session.getLine(i), this.regExp); + if (ranges.length > this.MAX_RANGES) + ranges = ranges.slice(0, this.MAX_RANGES); + ranges = ranges.map(function(match) { + return new Range(i, match.offset, i, match.offset + match.length); + }); + this.cache[i] = ranges.length ? ranges : ""; + } + for (var j = ranges.length; j--;) { + var rangeToAddMarkerTo = ranges[j].toScreenRange(session); + var rangeAsString = rangeToAddMarkerTo.toString(); + if (renderedMarkerRanges[rangeAsString]) + continue; + renderedMarkerRanges[rangeAsString] = true; + markerLayer.drawSingleLineMarker(html, rangeToAddMarkerTo, this.clazz, config); + } + } + }; + return SearchHighlight; + }()); + SearchHighlight.prototype.MAX_RANGES = 500; + exports.SearchHighlight = SearchHighlight; + +}); + +define("ace/undomanager", ["require", "exports", "module", "ace/range"], function(require, exports, module) { + "use strict"; + var UndoManager = /** @class */ (function() { + function UndoManager() { + this.$keepRedoStack; + this.$maxRev = 0; + this.$fromUndo = false; + this.$undoDepth = Infinity; + this.reset(); + } + UndoManager.prototype.addSession = function(session) { + this.$session = session; + }; + UndoManager.prototype.add = function(delta, allowMerge, session) { + if (this.$fromUndo) + return; + if (delta == this.$lastDelta) + return; + if (!this.$keepRedoStack) + this.$redoStack.length = 0; + if (allowMerge === false || !this.lastDeltas) { + this.lastDeltas = []; + var undoStackLength = this.$undoStack.length; + if (undoStackLength > this.$undoDepth - 1) { + this.$undoStack.splice(0, undoStackLength - this.$undoDepth + 1); + } + this.$undoStack.push(this.lastDeltas); + delta.id = this.$rev = ++this.$maxRev; + } + if (delta.action == "remove" || delta.action == "insert") + this.$lastDelta = delta; + this.lastDeltas.push(delta); + }; + UndoManager.prototype.addSelection = function(selection, rev) { + this.selections.push({ + value: selection, + rev: rev || this.$rev + }); + }; + UndoManager.prototype.startNewGroup = function() { + this.lastDeltas = null; + return this.$rev; + }; + UndoManager.prototype.markIgnored = function(from, to) { + if (to == null) + to = this.$rev + 1; + var stack = this.$undoStack; + for (var i = stack.length; i--;) { + var delta = stack[i][0]; + if (delta.id <= from) + break; + if (delta.id < to) + delta.ignore = true; + } + this.lastDeltas = null; + }; + UndoManager.prototype.getSelection = function(rev, after) { + var stack = this.selections; + for (var i = stack.length; i--;) { + var selection = stack[i]; + if (selection.rev < rev) { + if (after) + selection = stack[i + 1]; + return selection; + } + } + }; + UndoManager.prototype.getRevision = function() { + return this.$rev; + }; + UndoManager.prototype.getDeltas = function(from, to) { + if (to == null) + to = this.$rev + 1; + var stack = this.$undoStack; + var end = null, + start = 0; + for (var i = stack.length; i--;) { + var delta = stack[i][0]; + if (delta.id < to && !end) + end = i + 1; + if (delta.id <= from) { + start = i + 1; + break; + } + } + return stack.slice(start, end); + }; + UndoManager.prototype.getChangedRanges = function(from, to) { + if (to == null) + to = this.$rev + 1; + }; + UndoManager.prototype.getChangedLines = function(from, to) { + if (to == null) + to = this.$rev + 1; + }; + UndoManager.prototype.undo = function(session, dontSelect) { + this.lastDeltas = null; + var stack = this.$undoStack; + if (!rearrangeUndoStack(stack, stack.length)) + return; + if (!session) + session = this.$session; + if (this.$redoStackBaseRev !== this.$rev && this.$redoStack.length) + this.$redoStack = []; + this.$fromUndo = true; + var deltaSet = stack.pop(); + var undoSelectionRange = null; + if (deltaSet) { + undoSelectionRange = session.undoChanges(deltaSet, dontSelect); + this.$redoStack.push(deltaSet); + this.$syncRev(); + } + this.$fromUndo = false; + return undoSelectionRange; + }; + UndoManager.prototype.redo = function(session, dontSelect) { + this.lastDeltas = null; + if (!session) + session = this.$session; + this.$fromUndo = true; + if (this.$redoStackBaseRev != this.$rev) { + var diff = this.getDeltas(this.$redoStackBaseRev, this.$rev + 1); + rebaseRedoStack(this.$redoStack, diff); + this.$redoStackBaseRev = this.$rev; + this.$redoStack.forEach(function(x) { + x[0].id = ++this.$maxRev; + }, this); + } + var deltaSet = this.$redoStack.pop(); + var redoSelectionRange = null; + if (deltaSet) { + redoSelectionRange = session.redoChanges(deltaSet, dontSelect); + this.$undoStack.push(deltaSet); + this.$syncRev(); + } + this.$fromUndo = false; + return redoSelectionRange; + }; + UndoManager.prototype.$syncRev = function() { + var stack = this.$undoStack; + var nextDelta = stack[stack.length - 1]; + var id = nextDelta && nextDelta[0].id || 0; + this.$redoStackBaseRev = id; + this.$rev = id; + }; + UndoManager.prototype.reset = function() { + this.lastDeltas = null; + this.$lastDelta = null; + this.$undoStack = []; + this.$redoStack = []; + this.$rev = 0; + this.mark = 0; + this.$redoStackBaseRev = this.$rev; + this.selections = []; + }; + UndoManager.prototype.canUndo = function() { + return this.$undoStack.length > 0; + }; + UndoManager.prototype.canRedo = function() { + return this.$redoStack.length > 0; + }; + UndoManager.prototype.bookmark = function(rev) { + if (rev == undefined) + rev = this.$rev; + this.mark = rev; + }; + UndoManager.prototype.isAtBookmark = function() { + return this.$rev === this.mark; + }; + UndoManager.prototype.toJSON = function() { + return { + $redoStack: this.$redoStack, + $undoStack: this.$undoStack + }; + }; + UndoManager.prototype.fromJSON = function(json) { + this.reset(); + this.$undoStack = json.$undoStack; + this.$redoStack = json.$redoStack; + }; + UndoManager.prototype.$prettyPrint = function(delta) { + if (delta) + return stringifyDelta(delta); + return stringifyDelta(this.$undoStack) + "\n---\n" + stringifyDelta(this.$redoStack); + }; + return UndoManager; + }()); + UndoManager.prototype.hasUndo = UndoManager.prototype.canUndo; + UndoManager.prototype.hasRedo = UndoManager.prototype.canRedo; + UndoManager.prototype.isClean = UndoManager.prototype.isAtBookmark; + UndoManager.prototype.markClean = UndoManager.prototype.bookmark; + + function rearrangeUndoStack(stack, pos) { + for (var i = pos; i--;) { + var deltaSet = stack[i]; + if (deltaSet && !deltaSet[0].ignore) { + while (i < pos - 1) { + var swapped = swapGroups(stack[i], stack[i + 1]); + stack[i] = swapped[0]; + stack[i + 1] = swapped[1]; + i++; + } + return true; + } + } + } + var Range = require("./range").Range; + var cmp = Range.comparePoints; + var comparePoints = Range.comparePoints; + + function $updateMarkers(delta) { + var isInsert = delta.action == "insert"; + var start = delta.start; + var end = delta.end; + var rowShift = (end.row - start.row) * (isInsert ? 1 : -1); + var colShift = (end.column - start.column) * (isInsert ? 1 : -1); + if (isInsert) + end = start; + for (var i in this.marks) { + var point = this.marks[i]; + var cmp = comparePoints(point, start); + if (cmp < 0) { + continue; // delta starts after the range + } + if (cmp === 0) { + if (isInsert) { + if (point.bias == 1) { + cmp = 1; + } else { + point.bias == -1; + continue; + } + } + } + var cmp2 = isInsert ? cmp : comparePoints(point, end); + if (cmp2 > 0) { + point.row += rowShift; + point.column += point.row == end.row ? colShift : 0; + continue; + } + if (!isInsert && cmp2 <= 0) { + point.row = start.row; + point.column = start.column; + if (cmp2 === 0) + point.bias = 1; + } + } + } + + function clonePos(pos) { + return { + row: pos.row, + column: pos.column + }; + } + + function cloneDelta(d) { + return { + start: clonePos(d.start), + end: clonePos(d.end), + action: d.action, + lines: d.lines.slice() + }; + } + + function stringifyDelta(d) { + d = d || this; + if (Array.isArray(d)) { + return d.map(stringifyDelta).join("\n"); + } + var type = ""; + if (d.action) { + type = d.action == "insert" ? "+" : "-"; + type += "[" + d.lines + "]"; + } else if (d.value) { + if (Array.isArray(d.value)) { + type = d.value.map(stringifyRange).join("\n"); + } else { + type = stringifyRange(d.value); + } + } + if (d.start) { + type += stringifyRange(d); + } + if (d.id || d.rev) { + type += "\t(" + (d.id || d.rev) + ")"; + } + return type; + } + + function stringifyRange(r) { + return r.start.row + ":" + r.start.column + + "=>" + r.end.row + ":" + r.end.column; + } + + function swap(d1, d2) { + var i1 = d1.action == "insert"; + var i2 = d2.action == "insert"; + if (i1 && i2) { + if (cmp(d2.start, d1.end) >= 0) { + shift(d2, d1, -1); + } else if (cmp(d2.start, d1.start) <= 0) { + shift(d1, d2, +1); + } else { + return null; + } + } else if (i1 && !i2) { + if (cmp(d2.start, d1.end) >= 0) { + shift(d2, d1, -1); + } else if (cmp(d2.end, d1.start) <= 0) { + shift(d1, d2, -1); + } else { + return null; + } + } else if (!i1 && i2) { + if (cmp(d2.start, d1.start) >= 0) { + shift(d2, d1, +1); + } else if (cmp(d2.start, d1.start) <= 0) { + shift(d1, d2, +1); + } else { + return null; + } + } else if (!i1 && !i2) { + if (cmp(d2.start, d1.start) >= 0) { + shift(d2, d1, +1); + } else if (cmp(d2.end, d1.start) <= 0) { + shift(d1, d2, -1); + } else { + return null; + } + } + return [d2, d1]; + } + + function swapGroups(ds1, ds2) { + for (var i = ds1.length; i--;) { + for (var j = 0; j < ds2.length; j++) { + if (!swap(ds1[i], ds2[j])) { + while (i < ds1.length) { + while (j--) { + swap(ds2[j], ds1[i]); + } + j = ds2.length; + i++; + } + return [ds1, ds2]; + } + } + } + ds1.selectionBefore = ds2.selectionBefore = + ds1.selectionAfter = ds2.selectionAfter = null; + return [ds2, ds1]; + } + + function xform(d1, c1) { + var i1 = d1.action == "insert"; + var i2 = c1.action == "insert"; + if (i1 && i2) { + if (cmp(d1.start, c1.start) < 0) { + shift(c1, d1, 1); + } else { + shift(d1, c1, 1); + } + } else if (i1 && !i2) { + if (cmp(d1.start, c1.end) >= 0) { + shift(d1, c1, -1); + } else if (cmp(d1.start, c1.start) <= 0) { + shift(c1, d1, +1); + } else { + shift(d1, Range.fromPoints(c1.start, d1.start), -1); + shift(c1, d1, +1); + } + } else if (!i1 && i2) { + if (cmp(c1.start, d1.end) >= 0) { + shift(c1, d1, -1); + } else if (cmp(c1.start, d1.start) <= 0) { + shift(d1, c1, +1); + } else { + shift(c1, Range.fromPoints(d1.start, c1.start), -1); + shift(d1, c1, +1); + } + } else if (!i1 && !i2) { + if (cmp(c1.start, d1.end) >= 0) { + shift(c1, d1, -1); + } else if (cmp(c1.end, d1.start) <= 0) { + shift(d1, c1, -1); + } else { + var before, after; + if (cmp(d1.start, c1.start) < 0) { + before = d1; + d1 = splitDelta(d1, c1.start); + } + if (cmp(d1.end, c1.end) > 0) { + after = splitDelta(d1, c1.end); + } + shiftPos(c1.end, d1.start, d1.end, -1); + if (after && !before) { + d1.lines = after.lines; + d1.start = after.start; + d1.end = after.end; + after = d1; + } + return [c1, before, after].filter(Boolean); + } + } + return [c1, d1]; + } + + function shift(d1, d2, dir) { + shiftPos(d1.start, d2.start, d2.end, dir); + shiftPos(d1.end, d2.start, d2.end, dir); + } + + function shiftPos(pos, start, end, dir) { + if (pos.row == (dir == 1 ? start : end).row) { + pos.column += dir * (end.column - start.column); + } + pos.row += dir * (end.row - start.row); + } + + function splitDelta(c, pos) { + var lines = c.lines; + var end = c.end; + c.end = clonePos(pos); + var rowsBefore = c.end.row - c.start.row; + var otherLines = lines.splice(rowsBefore, lines.length); + var col = rowsBefore ? pos.column : pos.column - c.start.column; + lines.push(otherLines[0].substring(0, col)); + otherLines[0] = otherLines[0].substr(col); + var rest = { + start: clonePos(pos), + end: end, + lines: otherLines, + action: c.action + }; + return rest; + } + + function moveDeltasByOne(redoStack, d) { + d = cloneDelta(d); + for (var j = redoStack.length; j--;) { + var deltaSet = redoStack[j]; + for (var i = 0; i < deltaSet.length; i++) { + var x = deltaSet[i]; + var xformed = xform(x, d); + d = xformed[0]; + if (xformed.length != 2) { + if (xformed[2]) { + deltaSet.splice(i + 1, 1, xformed[1], xformed[2]); + i++; + } else if (!xformed[1]) { + deltaSet.splice(i, 1); + i--; + } + } + } + if (!deltaSet.length) { + redoStack.splice(j, 1); + } + } + return redoStack; + } + + function rebaseRedoStack(redoStack, deltaSets) { + for (var i = 0; i < deltaSets.length; i++) { + var deltas = deltaSets[i]; + for (var j = 0; j < deltas.length; j++) { + moveDeltasByOne(redoStack, deltas[j]); + } + } + } + exports.UndoManager = UndoManager; + +}); + +define("ace/edit_session/fold_line", ["require", "exports", "module", "ace/range"], function(require, exports, module) { + "use strict"; + var Range = require("../range").Range; + var FoldLine = /** @class */ (function() { + function FoldLine(foldData, folds) { + this.foldData = foldData; + if (Array.isArray(folds)) { + this.folds = folds; + } else { + folds = this.folds = [folds]; + } + var last = folds[folds.length - 1]; + this.range = new Range(folds[0].start.row, folds[0].start.column, last.end.row, last.end.column); + this.start = this.range.start; + this.end = this.range.end; + this.folds.forEach(function(fold) { + fold.setFoldLine(this); + }, this); + } + FoldLine.prototype.shiftRow = function(shift) { + this.start.row += shift; + this.end.row += shift; + this.folds.forEach(function(fold) { + fold.start.row += shift; + fold.end.row += shift; + }); + }; + FoldLine.prototype.addFold = function(fold) { + if (fold.sameRow) { + if (fold.start.row < this.startRow || fold.endRow > this.endRow) { + throw new Error("Can't add a fold to this FoldLine as it has no connection"); + } + this.folds.push(fold); + this.folds.sort(function(a, b) { + return -a.range.compareEnd(b.start.row, b.start.column); + }); + if (this.range.compareEnd(fold.start.row, fold.start.column) > 0) { + this.end.row = fold.end.row; + this.end.column = fold.end.column; + } else if (this.range.compareStart(fold.end.row, fold.end.column) < 0) { + this.start.row = fold.start.row; + this.start.column = fold.start.column; + } + } else if (fold.start.row == this.end.row) { + this.folds.push(fold); + this.end.row = fold.end.row; + this.end.column = fold.end.column; + } else if (fold.end.row == this.start.row) { + this.folds.unshift(fold); + this.start.row = fold.start.row; + this.start.column = fold.start.column; + } else { + throw new Error("Trying to add fold to FoldRow that doesn't have a matching row"); + } + fold.foldLine = this; + }; + FoldLine.prototype.containsRow = function(row) { + return row >= this.start.row && row <= this.end.row; + }; + FoldLine.prototype.walk = function(callback, endRow, endColumn) { + var lastEnd = 0, + folds = this.folds, + fold, cmp, stop, isNewRow = true; + if (endRow == null) { + endRow = this.end.row; + endColumn = this.end.column; + } + for (var i = 0; i < folds.length; i++) { + fold = folds[i]; + cmp = fold.range.compareStart(endRow, endColumn); + if (cmp == -1) { + callback(null, endRow, endColumn, lastEnd, isNewRow); + return; + } + stop = callback(null, fold.start.row, fold.start.column, lastEnd, isNewRow); + stop = !stop && callback(fold.placeholder, fold.start.row, fold.start.column, lastEnd); + if (stop || cmp === 0) { + return; + } + isNewRow = !fold.sameRow; + lastEnd = fold.end.column; + } + callback(null, endRow, endColumn, lastEnd, isNewRow); + }; + FoldLine.prototype.getNextFoldTo = function(row, column) { + var fold, cmp; + for (var i = 0; i < this.folds.length; i++) { + fold = this.folds[i]; + cmp = fold.range.compareEnd(row, column); + if (cmp == -1) { + return { + fold: fold, + kind: "after" + }; + } else if (cmp === 0) { + return { + fold: fold, + kind: "inside" + }; + } + } + return null; + }; + FoldLine.prototype.addRemoveChars = function(row, column, len) { + var ret = this.getNextFoldTo(row, column), + fold, folds; + if (ret) { + fold = ret.fold; + if (ret.kind == "inside" && + fold.start.column != column && + fold.start.row != row) { + window.console && window.console.log(row, column, fold); + } else if (fold.start.row == row) { + folds = this.folds; + var i = folds.indexOf(fold); + if (i === 0) { + this.start.column += len; + } + for (i; i < folds.length; i++) { + fold = folds[i]; + fold.start.column += len; + if (!fold.sameRow) { + return; + } + fold.end.column += len; + } + this.end.column += len; + } + } + }; + FoldLine.prototype.split = function(row, column) { + var pos = this.getNextFoldTo(row, column); + if (!pos || pos.kind == "inside") + return null; + var fold = pos.fold; + var folds = this.folds; + var foldData = this.foldData; + var i = folds.indexOf(fold); + var foldBefore = folds[i - 1]; + this.end.row = foldBefore.end.row; + this.end.column = foldBefore.end.column; + folds = folds.splice(i, folds.length - i); + var newFoldLine = new FoldLine(foldData, folds); + foldData.splice(foldData.indexOf(this) + 1, 0, newFoldLine); + return newFoldLine; + }; + FoldLine.prototype.merge = function(foldLineNext) { + var folds = foldLineNext.folds; + for (var i = 0; i < folds.length; i++) { + this.addFold(folds[i]); + } + var foldData = this.foldData; + foldData.splice(foldData.indexOf(foldLineNext), 1); + }; + FoldLine.prototype.toString = function() { + var ret = [this.range.toString() + ": ["]; + this.folds.forEach(function(fold) { + ret.push(" " + fold.toString()); + }); + ret.push("]"); + return ret.join("\n"); + }; + FoldLine.prototype.idxToPosition = function(idx) { + var lastFoldEndColumn = 0; + for (var i = 0; i < this.folds.length; i++) { + var fold = this.folds[i]; + idx -= fold.start.column - lastFoldEndColumn; + if (idx < 0) { + return { + row: fold.start.row, + column: fold.start.column + idx + }; + } + idx -= fold.placeholder.length; + if (idx < 0) { + return fold.start; + } + lastFoldEndColumn = fold.end.column; + } + return { + row: this.end.row, + column: this.end.column + idx + }; + }; + return FoldLine; + }()); + exports.FoldLine = FoldLine; + +}); + +define("ace/range_list", ["require", "exports", "module", "ace/range"], function(require, exports, module) { + "use strict"; + var Range = require("./range").Range; + var comparePoints = Range.comparePoints; + var RangeList = /** @class */ (function() { + function RangeList() { + this.ranges = []; + this.$bias = 1; + } + RangeList.prototype.pointIndex = function(pos, excludeEdges, startIndex) { + var list = this.ranges; + for (var i = startIndex || 0; i < list.length; i++) { + var range = list[i]; + var cmpEnd = comparePoints(pos, range.end); + if (cmpEnd > 0) + continue; + var cmpStart = comparePoints(pos, range.start); + if (cmpEnd === 0) + return excludeEdges && cmpStart !== 0 ? -i - 2 : i; + if (cmpStart > 0 || (cmpStart === 0 && !excludeEdges)) + return i; + return -i - 1; + } + return -i - 1; + }; + RangeList.prototype.add = function(range) { + var excludeEdges = !range.isEmpty(); + var startIndex = this.pointIndex(range.start, excludeEdges); + if (startIndex < 0) + startIndex = -startIndex - 1; + var endIndex = this.pointIndex(range.end, excludeEdges, startIndex); + if (endIndex < 0) + endIndex = -endIndex - 1; + else + endIndex++; + return this.ranges.splice(startIndex, endIndex - startIndex, range); + }; + RangeList.prototype.addList = function(list) { + var removed = []; + for (var i = list.length; i--;) { + removed.push.apply(removed, this.add(list[i])); + } + return removed; + }; + RangeList.prototype.substractPoint = function(pos) { + var i = this.pointIndex(pos); + if (i >= 0) + return this.ranges.splice(i, 1); + }; + RangeList.prototype.merge = function() { + var removed = []; + var list = this.ranges; + list = list.sort(function(a, b) { + return comparePoints(a.start, b.start); + }); + var next = list[0], + range; + for (var i = 1; i < list.length; i++) { + range = next; + next = list[i]; + var cmp = comparePoints(range.end, next.start); + if (cmp < 0) + continue; + if (cmp == 0 && !range.isEmpty() && !next.isEmpty()) + continue; + if (comparePoints(range.end, next.end) < 0) { + range.end.row = next.end.row; + range.end.column = next.end.column; + } + list.splice(i, 1); + removed.push(next); + next = range; + i--; + } + this.ranges = list; + return removed; + }; + RangeList.prototype.contains = function(row, column) { + return this.pointIndex({ + row: row, + column: column + }) >= 0; + }; + RangeList.prototype.containsPoint = function(pos) { + return this.pointIndex(pos) >= 0; + }; + RangeList.prototype.rangeAtPoint = function(pos) { + var i = this.pointIndex(pos); + if (i >= 0) + return this.ranges[i]; + }; + RangeList.prototype.clipRows = function(startRow, endRow) { + var list = this.ranges; + if (list[0].start.row > endRow || list[list.length - 1].start.row < startRow) + return []; + var startIndex = this.pointIndex({ + row: startRow, + column: 0 + }); + if (startIndex < 0) + startIndex = -startIndex - 1; + var endIndex = this.pointIndex({ + row: endRow, + column: 0 + }, startIndex); + if (endIndex < 0) + endIndex = -endIndex - 1; + var clipped = []; + for (var i = startIndex; i < endIndex; i++) { + clipped.push(list[i]); + } + return clipped; + }; + RangeList.prototype.removeAll = function() { + return this.ranges.splice(0, this.ranges.length); + }; + RangeList.prototype.attach = function(session) { + if (this.session) + this.detach(); + this.session = session; + this.onChange = this.$onChange.bind(this); + this.session.on('change', this.onChange); + }; + RangeList.prototype.detach = function() { + if (!this.session) + return; + this.session.removeListener('change', this.onChange); + this.session = null; + }; + RangeList.prototype.$onChange = function(delta) { + var start = delta.start; + var end = delta.end; + var startRow = start.row; + var endRow = end.row; + var ranges = this.ranges; + for (var i = 0, n = ranges.length; i < n; i++) { + var r = ranges[i]; + if (r.end.row >= startRow) + break; + } + if (delta.action == "insert") { + var lineDif = endRow - startRow; + var colDiff = -start.column + end.column; + for (; i < n; i++) { + var r = ranges[i]; + if (r.start.row > startRow) + break; + if (r.start.row == startRow && r.start.column >= start.column) { + if (r.start.column == start.column && this.$bias <= 0) {} else { + r.start.column += colDiff; + r.start.row += lineDif; + } + } + if (r.end.row == startRow && r.end.column >= start.column) { + if (r.end.column == start.column && this.$bias < 0) { + continue; + } + if (r.end.column == start.column && colDiff > 0 && i < n - 1) { + if (r.end.column > r.start.column && r.end.column == ranges[i + 1].start.column) + r.end.column -= colDiff; + } + r.end.column += colDiff; + r.end.row += lineDif; + } + } + } else { + var lineDif = startRow - endRow; + var colDiff = start.column - end.column; + for (; i < n; i++) { + var r = ranges[i]; + if (r.start.row > endRow) + break; + if (r.end.row < endRow && + (startRow < r.end.row || + startRow == r.end.row && start.column < r.end.column)) { + r.end.row = startRow; + r.end.column = start.column; + } else if (r.end.row == endRow) { + if (r.end.column <= end.column) { + if (lineDif || r.end.column > start.column) { + r.end.column = start.column; + r.end.row = start.row; + } + } else { + r.end.column += colDiff; + r.end.row += lineDif; + } + } else if (r.end.row > endRow) { + r.end.row += lineDif; + } + if (r.start.row < endRow && + (startRow < r.start.row || + startRow == r.start.row && start.column < r.start.column)) { + r.start.row = startRow; + r.start.column = start.column; + } else if (r.start.row == endRow) { + if (r.start.column <= end.column) { + if (lineDif || r.start.column > start.column) { + r.start.column = start.column; + r.start.row = start.row; + } + } else { + r.start.column += colDiff; + r.start.row += lineDif; + } + } else if (r.start.row > endRow) { + r.start.row += lineDif; + } + } + } + if (lineDif != 0 && i < n) { + for (; i < n; i++) { + var r = ranges[i]; + r.start.row += lineDif; + r.end.row += lineDif; + } + } + }; + return RangeList; + }()); + RangeList.prototype.comparePoints = comparePoints; + exports.RangeList = RangeList; + +}); + +define("ace/edit_session/fold", ["require", "exports", "module", "ace/range_list"], function(require, exports, module) { + "use strict"; + var __extends = (this && this.__extends) || (function() { + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ + __proto__: [] + } + instanceof Array && function(d, b) { + d.__proto__ = b; + }) || + function(d, b) { + for (var p in b) + if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); + }; + return function(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + var RangeList = require("../range_list").RangeList; + var Fold = /** @class */ (function(_super) { + __extends(Fold, _super); + + function Fold(range, placeholder) { + var _this = _super.call(this) || this; + _this.foldLine = null; + _this.placeholder = placeholder; + _this.range = range; + _this.start = range.start; + _this.end = range.end; + _this.sameRow = range.start.row == range.end.row; + _this.subFolds = _this.ranges = []; + return _this; + } + Fold.prototype.toString = function() { + return '"' + this.placeholder + '" ' + this.range.toString(); + }; + Fold.prototype.setFoldLine = function(foldLine) { + this.foldLine = foldLine; + this.subFolds.forEach(function(fold) { + fold.setFoldLine(foldLine); + }); + }; + Fold.prototype.clone = function() { + var range = this.range.clone(); + var fold = new Fold(range, this.placeholder); + this.subFolds.forEach(function(subFold) { + fold.subFolds.push(subFold.clone()); + }); + fold.collapseChildren = this.collapseChildren; + return fold; + }; + Fold.prototype.addSubFold = function(fold) { + if (this.range.isEqual(fold)) + return; + consumeRange(fold, this.start); + var row = fold.start.row, + column = fold.start.column; + for (var i = 0, cmp = -1; i < this.subFolds.length; i++) { + cmp = this.subFolds[i].range.compare(row, column); + if (cmp != 1) + break; + } + var afterStart = this.subFolds[i]; + var firstConsumed = 0; + if (cmp == 0) { + if (afterStart.range.containsRange(fold)) + return afterStart.addSubFold(fold); + else + firstConsumed = 1; + } + var row = fold.range.end.row, + column = fold.range.end.column; + for (var j = i, cmp = -1; j < this.subFolds.length; j++) { + cmp = this.subFolds[j].range.compare(row, column); + if (cmp != 1) + break; + } + if (cmp == 0) + j++; + var consumedFolds = this.subFolds.splice(i, j - i, fold); + var last = cmp == 0 ? consumedFolds.length - 1 : consumedFolds.length; + for (var k = firstConsumed; k < last; k++) { + fold.addSubFold(consumedFolds[k]); + } + fold.setFoldLine(this.foldLine); + return fold; + }; + Fold.prototype.restoreRange = function(range) { + return restoreRange(range, this.start); + }; + return Fold; + }(RangeList)); + + function consumePoint(point, anchor) { + point.row -= anchor.row; + if (point.row == 0) + point.column -= anchor.column; + } + + function consumeRange(range, anchor) { + consumePoint(range.start, anchor); + consumePoint(range.end, anchor); + } + + function restorePoint(point, anchor) { + if (point.row == 0) + point.column += anchor.column; + point.row += anchor.row; + } + + function restoreRange(range, anchor) { + restorePoint(range.start, anchor); + restorePoint(range.end, anchor); + } + exports.Fold = Fold; + +}); + +define("ace/edit_session/folding", ["require", "exports", "module", "ace/range", "ace/edit_session/fold_line", "ace/edit_session/fold", "ace/token_iterator", "ace/mouse/mouse_event"], function(require, exports, module) { // @ts-nocheck + "use strict"; + var Range = require("../range").Range; + var FoldLine = require("./fold_line").FoldLine; + var Fold = require("./fold").Fold; + var TokenIterator = require("../token_iterator").TokenIterator; + var MouseEvent = require("../mouse/mouse_event").MouseEvent; + + function Folding() { + this.getFoldAt = function(row, column, side) { + var foldLine = this.getFoldLine(row); + if (!foldLine) + return null; + var folds = foldLine.folds; + for (var i = 0; i < folds.length; i++) { + var range = folds[i].range; + if (range.contains(row, column)) { + if (side == 1 && range.isEnd(row, column) && !range.isEmpty()) { + continue; + } else if (side == -1 && range.isStart(row, column) && !range.isEmpty()) { + continue; + } + return folds[i]; + } + } + }; + this.getFoldsInRange = function(range) { + var start = range.start; + var end = range.end; + var foldLines = this.$foldData; + var foundFolds = []; + start.column += 1; + end.column -= 1; + for (var i = 0; i < foldLines.length; i++) { + var cmp = foldLines[i].range.compareRange(range); + if (cmp == 2) { + continue; + } else if (cmp == -2) { + break; + } + var folds = foldLines[i].folds; + for (var j = 0; j < folds.length; j++) { + var fold = folds[j]; + cmp = fold.range.compareRange(range); + if (cmp == -2) { + break; + } else if (cmp == 2) { + continue; + } else + if (cmp == 42) { + break; + } + foundFolds.push(fold); + } + } + start.column -= 1; + end.column += 1; + return foundFolds; + }; + this.getFoldsInRangeList = function(ranges) { + if (Array.isArray(ranges)) { + var folds = []; + ranges.forEach(function(range) { + folds = folds.concat(this.getFoldsInRange(range)); + }, this); + } else { + var folds = this.getFoldsInRange(ranges); + } + return folds; + }; + this.getAllFolds = function() { + var folds = []; + var foldLines = this.$foldData; + for (var i = 0; i < foldLines.length; i++) + for (var j = 0; j < foldLines[i].folds.length; j++) + folds.push(foldLines[i].folds[j]); + return folds; + }; + this.getFoldStringAt = function(row, column, trim, foldLine) { + foldLine = foldLine || this.getFoldLine(row); + if (!foldLine) + return null; + var lastFold = { + end: { + column: 0 + } + }; + var str, fold; + for (var i = 0; i < foldLine.folds.length; i++) { + fold = foldLine.folds[i]; + var cmp = fold.range.compareEnd(row, column); + if (cmp == -1) { + str = this + .getLine(fold.start.row) + .substring(lastFold.end.column, fold.start.column); + break; + } else if (cmp === 0) { + return null; + } + lastFold = fold; + } + if (!str) + str = this.getLine(fold.start.row).substring(lastFold.end.column); + if (trim == -1) + return str.substring(0, column - lastFold.end.column); + else if (trim == 1) + return str.substring(column - lastFold.end.column); + else + return str; + }; + this.getFoldLine = function(docRow, startFoldLine) { + var foldData = this.$foldData; + var i = 0; + if (startFoldLine) + i = foldData.indexOf(startFoldLine); + if (i == -1) + i = 0; + for (i; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (foldLine.start.row <= docRow && foldLine.end.row >= docRow) { + return foldLine; + } else if (foldLine.end.row > docRow) { + return null; + } + } + return null; + }; + this.getNextFoldLine = function(docRow, startFoldLine) { + var foldData = this.$foldData; + var i = 0; + if (startFoldLine) + i = foldData.indexOf(startFoldLine); + if (i == -1) + i = 0; + for (i; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (foldLine.end.row >= docRow) { + return foldLine; + } + } + return null; + }; + this.getFoldedRowCount = function(first, last) { + var foldData = this.$foldData, + rowCount = last - first + 1; + for (var i = 0; i < foldData.length; i++) { + var foldLine = foldData[i], + end = foldLine.end.row, + start = foldLine.start.row; + if (end >= last) { + if (start < last) { + if (start >= first) + rowCount -= last - start; + else + rowCount = 0; // in one fold + } + break; + } else if (end >= first) { + if (start >= first) // fold inside range + rowCount -= end - start; + else + rowCount -= end - first + 1; + } + } + return rowCount; + }; + this.$addFoldLine = function(foldLine) { + this.$foldData.push(foldLine); + this.$foldData.sort(function(a, b) { + return a.start.row - b.start.row; + }); + return foldLine; + }; + this.addFold = function(placeholder, range) { + var foldData = this.$foldData; + var added = false; + var fold; + if (placeholder instanceof Fold) + fold = placeholder; + else { + fold = new Fold(range, placeholder); + fold.collapseChildren = range.collapseChildren; + } + this.$clipRangeToDocument(fold.range); + var startRow = fold.start.row; + var startColumn = fold.start.column; + var endRow = fold.end.row; + var endColumn = fold.end.column; + var startFold = this.getFoldAt(startRow, startColumn, 1); + var endFold = this.getFoldAt(endRow, endColumn, -1); + if (startFold && endFold == startFold) + return startFold.addSubFold(fold); + if (startFold && !startFold.range.isStart(startRow, startColumn)) + this.removeFold(startFold); + if (endFold && !endFold.range.isEnd(endRow, endColumn)) + this.removeFold(endFold); + var folds = this.getFoldsInRange(fold.range); + if (folds.length > 0) { + this.removeFolds(folds); + if (!fold.collapseChildren) { + folds.forEach(function(subFold) { + fold.addSubFold(subFold); + }); + } + } + for (var i = 0; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (endRow == foldLine.start.row) { + foldLine.addFold(fold); + added = true; + break; + } else if (startRow == foldLine.end.row) { + foldLine.addFold(fold); + added = true; + if (!fold.sameRow) { + var foldLineNext = foldData[i + 1]; + if (foldLineNext && foldLineNext.start.row == endRow) { + foldLine.merge(foldLineNext); + break; + } + } + break; + } else if (endRow <= foldLine.start.row) { + break; + } + } + if (!added) + foldLine = this.$addFoldLine(new FoldLine(this.$foldData, fold)); + if (this.$useWrapMode) + this.$updateWrapData(foldLine.start.row, foldLine.start.row); + else + this.$updateRowLengthCache(foldLine.start.row, foldLine.start.row); + this.$modified = true; + this._signal("changeFold", { + data: fold, + action: "add" + }); + return fold; + }; + this.addFolds = function(folds) { + folds.forEach(function(fold) { + this.addFold(fold); + }, this); + }; + this.removeFold = function(fold) { + var foldLine = fold.foldLine; + var startRow = foldLine.start.row; + var endRow = foldLine.end.row; + var foldLines = this.$foldData; + var folds = foldLine.folds; + if (folds.length == 1) { + foldLines.splice(foldLines.indexOf(foldLine), 1); + } else + if (foldLine.range.isEnd(fold.end.row, fold.end.column)) { + folds.pop(); + foldLine.end.row = folds[folds.length - 1].end.row; + foldLine.end.column = folds[folds.length - 1].end.column; + } else + if (foldLine.range.isStart(fold.start.row, fold.start.column)) { + folds.shift(); + foldLine.start.row = folds[0].start.row; + foldLine.start.column = folds[0].start.column; + } else + if (fold.sameRow) { + folds.splice(folds.indexOf(fold), 1); + } else { + var newFoldLine = foldLine.split(fold.start.row, fold.start.column); + folds = newFoldLine.folds; + folds.shift(); + newFoldLine.start.row = folds[0].start.row; + newFoldLine.start.column = folds[0].start.column; + } + if (!this.$updating) { + if (this.$useWrapMode) + this.$updateWrapData(startRow, endRow); + else + this.$updateRowLengthCache(startRow, endRow); + } + this.$modified = true; + this._signal("changeFold", { + data: fold, + action: "remove" + }); + }; + this.removeFolds = function(folds) { + var cloneFolds = []; + for (var i = 0; i < folds.length; i++) { + cloneFolds.push(folds[i]); + } + cloneFolds.forEach(function(fold) { + this.removeFold(fold); + }, this); + this.$modified = true; + }; + this.expandFold = function(fold) { + this.removeFold(fold); + fold.subFolds.forEach(function(subFold) { + fold.restoreRange(subFold); + this.addFold(subFold); + }, this); + if (fold.collapseChildren > 0) { + this.foldAll(fold.start.row + 1, fold.end.row, fold.collapseChildren - 1); + } + fold.subFolds = []; + }; + this.expandFolds = function(folds) { + folds.forEach(function(fold) { + this.expandFold(fold); + }, this); + }; + this.unfold = function(location, expandInner) { + var range, folds; + if (location == null) { + range = new Range(0, 0, this.getLength(), 0); + if (expandInner == null) + expandInner = true; + } else if (typeof location == "number") { + range = new Range(location, 0, location, this.getLine(location).length); + } else if ("row" in location) { + range = Range.fromPoints(location, location); + } else if (Array.isArray(location)) { + folds = []; + location.forEach(function(range) { + folds = folds.concat(this.unfold(range)); + }, this); + return folds; + } else { + range = location; + } + folds = this.getFoldsInRangeList(range); + var outermostFolds = folds; + while (folds.length == 1 && + Range.comparePoints(folds[0].start, range.start) < 0 && + Range.comparePoints(folds[0].end, range.end) > 0) { + this.expandFolds(folds); + folds = this.getFoldsInRangeList(range); + } + if (expandInner != false) { + this.removeFolds(folds); + } else { + this.expandFolds(folds); + } + if (outermostFolds.length) + return outermostFolds; + }; + this.isRowFolded = function(docRow, startFoldRow) { + return !!this.getFoldLine(docRow, startFoldRow); + }; + this.getRowFoldEnd = function(docRow, startFoldRow) { + var foldLine = this.getFoldLine(docRow, startFoldRow); + return foldLine ? foldLine.end.row : docRow; + }; + this.getRowFoldStart = function(docRow, startFoldRow) { + var foldLine = this.getFoldLine(docRow, startFoldRow); + return foldLine ? foldLine.start.row : docRow; + }; + this.getFoldDisplayLine = function(foldLine, endRow, endColumn, startRow, startColumn) { + if (startRow == null) + startRow = foldLine.start.row; + if (startColumn == null) + startColumn = 0; + if (endRow == null) + endRow = foldLine.end.row; + if (endColumn == null) + endColumn = this.getLine(endRow).length; + var doc = this.doc; + var textLine = ""; + foldLine.walk(function(placeholder, row, column, lastColumn) { + if (row < startRow) + return; + if (row == startRow) { + if (column < startColumn) + return; + lastColumn = Math.max(startColumn, lastColumn); + } + if (placeholder != null) { + textLine += placeholder; + } else { + textLine += doc.getLine(row).substring(lastColumn, column); + } + }, endRow, endColumn); + return textLine; + }; + this.getDisplayLine = function(row, endColumn, startRow, startColumn) { + var foldLine = this.getFoldLine(row); + if (!foldLine) { + var line; + line = this.doc.getLine(row); + return line.substring(startColumn || 0, endColumn || line.length); + } else { + return this.getFoldDisplayLine(foldLine, row, endColumn, startRow, startColumn); + } + }; + this.$cloneFoldData = function() { + var fd = []; + fd = this.$foldData.map(function(foldLine) { + var folds = foldLine.folds.map(function(fold) { + return fold.clone(); + }); + return new FoldLine(fd, folds); + }); + return fd; + }; + this.toggleFold = function(tryToUnfold) { + var selection = this.selection; + var range = selection.getRange(); + var fold; + var bracketPos; + if (range.isEmpty()) { + var cursor = range.start; + fold = this.getFoldAt(cursor.row, cursor.column); + if (fold) { + this.expandFold(fold); + return; + } else if (bracketPos = this.findMatchingBracket(cursor)) { + if (range.comparePoint(bracketPos) == 1) { + range.end = bracketPos; + } else { + range.start = bracketPos; + range.start.column++; + range.end.column--; + } + } else if (bracketPos = this.findMatchingBracket({ + row: cursor.row, + column: cursor.column + 1 + })) { + if (range.comparePoint(bracketPos) == 1) + range.end = bracketPos; + else + range.start = bracketPos; + range.start.column++; + } else { + range = this.getCommentFoldRange(cursor.row, cursor.column) || range; + } + } else { + var folds = this.getFoldsInRange(range); + if (tryToUnfold && folds.length) { + this.expandFolds(folds); + return; + } else if (folds.length == 1) { + fold = folds[0]; + } + } + if (!fold) + fold = this.getFoldAt(range.start.row, range.start.column); + if (fold && fold.range.toString() == range.toString()) { + this.expandFold(fold); + return; + } + var placeholder = "..."; + if (!range.isMultiLine()) { + placeholder = this.getTextRange(range); + if (placeholder.length < 4) + return; + placeholder = placeholder.trim().substring(0, 2) + ".."; + } + this.addFold(placeholder, range); + }; + this.getCommentFoldRange = function(row, column, dir) { + var iterator = new TokenIterator(this, row, column); + var token = iterator.getCurrentToken(); + var type = token && token.type; + if (token && /^comment|string/.test(type)) { + type = type.match(/comment|string/)[0]; + if (type == "comment") + type += "|doc-start|\\.doc"; + var re = new RegExp(type); + var range = new Range(); + if (dir != 1) { + do { + token = iterator.stepBackward(); + } while (token && re.test(token.type)); + token = iterator.stepForward(); + } + range.start.row = iterator.getCurrentTokenRow(); + range.start.column = iterator.getCurrentTokenColumn() + token.value.length; + iterator = new TokenIterator(this, row, column); + var initState = this.getState(iterator.$row); + if (dir != -1) { + var lastRow = -1; + do { + token = iterator.stepForward(); + if (lastRow == -1) { + var state = this.getState(iterator.$row); + if (initState.toString() !== state.toString()) + lastRow = iterator.$row; + } else if (iterator.$row > lastRow) { + break; + } + } while (token && re.test(token.type)); + token = iterator.stepBackward(); + } else + token = iterator.getCurrentToken(); + range.end.row = iterator.getCurrentTokenRow(); + range.end.column = iterator.getCurrentTokenColumn(); + return range; + } + }; + this.foldAll = function(startRow, endRow, depth, test) { + if (depth == undefined) + depth = 100000; // JSON.stringify doesn't hanle Infinity + var foldWidgets = this.foldWidgets; + if (!foldWidgets) + return; // mode doesn't support folding + endRow = endRow || this.getLength(); + startRow = startRow || 0; + for (var row = startRow; row < endRow; row++) { + if (foldWidgets[row] == null) + foldWidgets[row] = this.getFoldWidget(row); + if (foldWidgets[row] != "start") + continue; + if (test && !test(row)) + continue; + var range = this.getFoldWidgetRange(row); + if (range && range.isMultiLine() && + range.end.row <= endRow && + range.start.row >= startRow) { + row = range.end.row; + range.collapseChildren = depth; + this.addFold("...", range); + } + } + }; + this.foldToLevel = function(level) { + this.foldAll(); + while (level-- > 0) + this.unfold(null, false); + }; + this.foldAllComments = function() { + var session = this; + this.foldAll(null, null, null, function(row) { + var tokens = session.getTokens(row); + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + if (token.type == "text" && /^\s+$/.test(token.value)) + continue; + if (/comment/.test(token.type)) + return true; + return false; + } + }); + }; + this.$foldStyles = { + "manual": 1, + "markbegin": 1, + "markbeginend": 1 + }; + this.$foldStyle = "markbegin"; + this.setFoldStyle = function(style) { + if (!this.$foldStyles[style]) + throw new Error("invalid fold style: " + style + "[" + Object.keys(this.$foldStyles).join(", ") + "]"); + if (this.$foldStyle == style) + return; + this.$foldStyle = style; + if (style == "manual") + this.unfold(); + var mode = this.$foldMode; + this.$setFolding(null); + this.$setFolding(mode); + }; + this.$setFolding = function(foldMode) { + if (this.$foldMode == foldMode) + return; + this.$foldMode = foldMode; + this.off('change', this.$updateFoldWidgets); + this.off('tokenizerUpdate', this.$tokenizerUpdateFoldWidgets); + this._signal("changeAnnotation"); + if (!foldMode || this.$foldStyle == "manual") { + this.foldWidgets = null; + return; + } + this.foldWidgets = []; + this.getFoldWidget = foldMode.getFoldWidget.bind(foldMode, this, this.$foldStyle); + this.getFoldWidgetRange = foldMode.getFoldWidgetRange.bind(foldMode, this, this.$foldStyle); + this.$updateFoldWidgets = this.updateFoldWidgets.bind(this); + this.$tokenizerUpdateFoldWidgets = this.tokenizerUpdateFoldWidgets.bind(this); + this.on('change', this.$updateFoldWidgets); + this.on('tokenizerUpdate', this.$tokenizerUpdateFoldWidgets); + }; + this.getParentFoldRangeData = function(row, ignoreCurrent) { + var fw = this.foldWidgets; + if (!fw || (ignoreCurrent && fw[row])) + return {}; + var i = row - 1, + firstRange; + while (i >= 0) { + var c = fw[i]; + if (c == null) + c = fw[i] = this.getFoldWidget(i); + if (c == "start") { + var range = this.getFoldWidgetRange(i); + if (!firstRange) + firstRange = range; + if (range && range.end.row >= row) + break; + } + i--; + } + return { + range: i !== -1 && range, + firstRange: firstRange + }; + }; + this.onFoldWidgetClick = function(row, e) { + if (e instanceof MouseEvent) + e = e.domEvent; + var options = { + children: e.shiftKey, + all: e.ctrlKey || e.metaKey, + siblings: e.altKey + }; + var range = this.$toggleFoldWidget(row, options); + if (!range) { + var el = (e.target || e.srcElement); + if (el && /ace_fold-widget/.test(el.className)) + el.className += " ace_invalid"; + } + }; + this.$toggleFoldWidget = function(row, options) { + if (!this.getFoldWidget) + return; + var type = this.getFoldWidget(row); + var line = this.getLine(row); + var dir = type === "end" ? -1 : 1; + var fold = this.getFoldAt(row, dir === -1 ? 0 : line.length, dir); + if (fold) { + if (options.children || options.all) + this.removeFold(fold); + else + this.expandFold(fold); + return fold; + } + var range = this.getFoldWidgetRange(row, true); + if (range && !range.isMultiLine()) { + fold = this.getFoldAt(range.start.row, range.start.column, 1); + if (fold && range.isEqual(fold.range)) { + this.removeFold(fold); + return fold; + } + } + if (options.siblings) { + var data = this.getParentFoldRangeData(row); + if (data.range) { + var startRow = data.range.start.row + 1; + var endRow = data.range.end.row; + } + this.foldAll(startRow, endRow, options.all ? 10000 : 0); + } else if (options.children) { + endRow = range ? range.end.row : this.getLength(); + this.foldAll(row + 1, endRow, options.all ? 10000 : 0); + } else if (range) { + if (options.all) + range.collapseChildren = 10000; + this.addFold("...", range); + } + return range; + }; + this.toggleFoldWidget = function(toggleParent) { + var row = this.selection.getCursor().row; + row = this.getRowFoldStart(row); + var range = this.$toggleFoldWidget(row, {}); + if (range) + return; + var data = this.getParentFoldRangeData(row, true); + range = data.range || data.firstRange; + if (range) { + row = range.start.row; + var fold = this.getFoldAt(row, this.getLine(row).length, 1); + if (fold) { + this.removeFold(fold); + } else { + this.addFold("...", range); + } + } + }; + this.updateFoldWidgets = function(delta) { + var firstRow = delta.start.row; + var len = delta.end.row - firstRow; + if (len === 0) { + this.foldWidgets[firstRow] = null; + } else if (delta.action == 'remove') { + this.foldWidgets.splice(firstRow, len + 1, null); + } else { + var args = Array(len + 1); + args.unshift(firstRow, 1); + this.foldWidgets.splice.apply(this.foldWidgets, args); + } + }; + this.tokenizerUpdateFoldWidgets = function(e) { + var rows = e.data; + if (rows.first != rows.last) { + if (this.foldWidgets.length > rows.first) + this.foldWidgets.splice(rows.first, this.foldWidgets.length); + } + }; + } + exports.Folding = Folding; + +}); + +define("ace/edit_session/bracket_match", ["require", "exports", "module", "ace/token_iterator", "ace/range"], function(require, exports, module) { + "use strict"; + var TokenIterator = require("../token_iterator").TokenIterator; + var Range = require("../range").Range; + + function BracketMatch() { + this.findMatchingBracket = function(position, chr) { + if (position.column == 0) + return null; + var charBeforeCursor = chr || this.getLine(position.row).charAt(position.column - 1); + if (charBeforeCursor == "") + return null; + var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/); + if (!match) + return null; + if (match[1]) + return this.$findClosingBracket(match[1], position); + else + return this.$findOpeningBracket(match[2], position); + }; + this.getBracketRange = function(pos) { + var line = this.getLine(pos.row); + var before = true, + range; + var chr = line.charAt(pos.column - 1); + var match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + if (!match) { + chr = line.charAt(pos.column); + pos = { + row: pos.row, + column: pos.column + 1 + }; + match = chr && chr.match(/([\(\[\{])|([\)\]\}])/); + before = false; + } + if (!match) + return null; + if (match[1]) { + var bracketPos = this.$findClosingBracket(match[1], pos); + if (!bracketPos) + return null; + range = Range.fromPoints(pos, bracketPos); + if (!before) { + range.end.column++; + range.start.column--; + } + range.cursor = range.end; + } else { + var bracketPos = this.$findOpeningBracket(match[2], pos); + if (!bracketPos) + return null; + range = Range.fromPoints(bracketPos, pos); + if (!before) { + range.start.column++; + range.end.column--; + } + range.cursor = range.start; + } + return range; + }; + this.getMatchingBracketRanges = function(pos, isBackwards) { + var line = this.getLine(pos.row); + var bracketsRegExp = /([\(\[\{])|([\)\]\}])/; + var chr = !isBackwards && line.charAt(pos.column - 1); + var match = chr && chr.match(bracketsRegExp); + if (!match) { + chr = (isBackwards === undefined || isBackwards) && line.charAt(pos.column); + pos = { + row: pos.row, + column: pos.column + 1 + }; + match = chr && chr.match(bracketsRegExp); + } + if (!match) + return null; + var startRange = new Range(pos.row, pos.column - 1, pos.row, pos.column); + var bracketPos = match[1] ? this.$findClosingBracket(match[1], pos) : + this.$findOpeningBracket(match[2], pos); + if (!bracketPos) + return [startRange]; + var endRange = new Range(bracketPos.row, bracketPos.column, bracketPos.row, bracketPos.column + 1); + return [startRange, endRange]; + }; + this.$brackets = { + ")": "(", + "(": ")", + "]": "[", + "[": "]", + "{": "}", + "}": "{", + "<": ">", + ">": "<" + }; + this.$findOpeningBracket = function(bracket, position, typeRe) { + var openBracket = this.$brackets[bracket]; + var depth = 1; + var iterator = new TokenIterator(this, position.row, position.column); + var token = iterator.getCurrentToken(); + if (!token) + token = iterator.stepForward(); + if (!token) + return; + if (!typeRe) { + typeRe = new RegExp("(\\.?" + + token.type.replace(".", "\\.").replace("rparen", ".paren") + .replace(/\b(?:end)\b/, "(?:start|begin|end)") + .replace(/-close\b/, "-(close|open)") + + ")+"); + } + var valueIndex = position.column - iterator.getCurrentTokenColumn() - 2; + var value = token.value; + while (true) { + while (valueIndex >= 0) { + var chr = value.charAt(valueIndex); + if (chr == openBracket) { + depth -= 1; + if (depth == 0) { + return { + row: iterator.getCurrentTokenRow(), + column: valueIndex + iterator.getCurrentTokenColumn() + }; + } + } else if (chr == bracket) { + depth += 1; + } + valueIndex -= 1; + } + do { + token = iterator.stepBackward(); + } while (token && !typeRe.test(token.type)); + if (token == null) + break; + value = token.value; + valueIndex = value.length - 1; + } + return null; + }; + this.$findClosingBracket = function(bracket, position, typeRe) { + var closingBracket = this.$brackets[bracket]; + var depth = 1; + var iterator = new TokenIterator(this, position.row, position.column); + var token = iterator.getCurrentToken(); + if (!token) + token = iterator.stepForward(); + if (!token) + return; + if (!typeRe) { + typeRe = new RegExp("(\\.?" + + token.type.replace(".", "\\.").replace("lparen", ".paren") + .replace(/\b(?:start|begin)\b/, "(?:start|begin|end)") + .replace(/-open\b/, "-(close|open)") + + ")+"); + } + var valueIndex = position.column - iterator.getCurrentTokenColumn(); + while (true) { + var value = token.value; + var valueLength = value.length; + while (valueIndex < valueLength) { + var chr = value.charAt(valueIndex); + if (chr == closingBracket) { + depth -= 1; + if (depth == 0) { + return { + row: iterator.getCurrentTokenRow(), + column: valueIndex + iterator.getCurrentTokenColumn() + }; + } + } else if (chr == bracket) { + depth += 1; + } + valueIndex += 1; + } + do { + token = iterator.stepForward(); + } while (token && !typeRe.test(token.type)); + if (token == null) + break; + valueIndex = 0; + } + return null; + }; + this.getMatchingTags = function(pos) { + var iterator = new TokenIterator(this, pos.row, pos.column); + var token = this.$findTagName(iterator); + if (!token) + return; + var prevToken = iterator.stepBackward(); + if (prevToken.value === '<') { + return this.$findClosingTag(iterator, token); + } else { + return this.$findOpeningTag(iterator, token); + } + }; + this.$findTagName = function(iterator) { + var token = iterator.getCurrentToken(); + var found = false; + var backward = false; + if (token && token.type.indexOf('tag-name') === -1) { + do { + if (backward) + token = iterator.stepBackward(); + else + token = iterator.stepForward(); + if (token) { + if (token.value === "/>") { + backward = true; + } else if (token.type.indexOf('tag-name') !== -1) { + found = true; + } + } + } while (token && !found); + } + return token; + }; + this.$findClosingTag = function(iterator, token) { + var prevToken; + var currentTag = token.value; + var tag = token.value; + var depth = 0; + var openTagStart = new Range(iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn(), iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn() + 1); + token = iterator.stepForward(); + var openTagName = new Range(iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn(), iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn() + token.value.length); + var foundOpenTagEnd = false; + do { + prevToken = token; + if (prevToken.type.indexOf('tag-close') !== -1 && !foundOpenTagEnd) { + var openTagEnd = new Range(iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn(), iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn() + 1); //Range for `>` + foundOpenTagEnd = true; + } + token = iterator.stepForward(); + if (token) { + if (token.value === '>' && !foundOpenTagEnd) { + var openTagEnd = new Range(iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn(), iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn() + 1); //Range for `>` + foundOpenTagEnd = true; + } + if (token.type.indexOf('tag-name') !== -1) { + currentTag = token.value; + if (tag === currentTag) { + if (prevToken.value === '<') { + depth++; + } else if (prevToken.value === '') { + var closeTagEnd = new Range(iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn(), iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn() + 1); //Range for > + } else { + return; + } + } + } + } + } else if (tag === currentTag && token.value === '/>') { // self-closing tag + depth--; + if (depth < 0) { //found self-closing tag end + var closeTagStart = new Range(iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn(), iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn() + 2); + var closeTagName = closeTagStart; + var closeTagEnd = closeTagName; + var openTagEnd = new Range(openTagName.end.row, openTagName.end.column, openTagName.end.row, openTagName.end.column + 1); + } + } + } + } while (token && depth >= 0); + if (openTagStart && openTagEnd && closeTagStart && closeTagEnd && openTagName && closeTagName) { + return { + openTag: new Range(openTagStart.start.row, openTagStart.start.column, openTagEnd.end.row, openTagEnd.end.column), + closeTag: new Range(closeTagStart.start.row, closeTagStart.start.column, closeTagEnd.end.row, closeTagEnd.end.column), + openTagName: openTagName, + closeTagName: closeTagName + }; + } + }; + this.$findOpeningTag = function(iterator, token) { + var prevToken = iterator.getCurrentToken(); + var tag = token.value; + var depth = 0; + var startRow = iterator.getCurrentTokenRow(); + var startColumn = iterator.getCurrentTokenColumn(); + var endColumn = startColumn + 2; + var closeTagStart = new Range(startRow, startColumn, startRow, endColumn); //Range for ") + return; + var closeTagEnd = new Range(iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn(), iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn() + 1); //Range for > + iterator.stepBackward(); + iterator.stepBackward(); + do { + token = prevToken; + startRow = iterator.getCurrentTokenRow(); + startColumn = iterator.getCurrentTokenColumn(); + endColumn = startColumn + token.value.length; + prevToken = iterator.stepBackward(); + if (token) { + if (token.type.indexOf('tag-name') !== -1) { + if (tag === token.value) { + if (prevToken.value === '<') { + depth++; + if (depth > 0) { //found opening tag + var openTagName = new Range(startRow, startColumn, startRow, endColumn); + var openTagStart = new Range(iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn(), iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn() + 1); //Range for < + do { + token = iterator.stepForward(); + } while (token && token.value !== '>'); + var openTagEnd = new Range(iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn(), iterator.getCurrentTokenRow(), iterator.getCurrentTokenColumn() + 1); //Range for > + } + } else if (prevToken.value === '') { // self-closing tag + var stepCount = 0; + var tmpToken = prevToken; + while (tmpToken) { + if (tmpToken.type.indexOf('tag-name') !== -1 && tmpToken.value === tag) { + depth--; + break; + } else if (tmpToken.value === '<') { + break; + } + tmpToken = iterator.stepBackward(); + stepCount++; + } + for (var i = 0; i < stepCount; i++) { + iterator.stepForward(); + } + } + } + } while (prevToken && depth <= 0); + if (openTagStart && openTagEnd && closeTagStart && closeTagEnd && openTagName && closeTagName) { + return { + openTag: new Range(openTagStart.start.row, openTagStart.start.column, openTagEnd.end.row, openTagEnd.end.column), + closeTag: new Range(closeTagStart.start.row, closeTagStart.start.column, closeTagEnd.end.row, closeTagEnd.end.column), + openTagName: openTagName, + closeTagName: closeTagName + }; + } + }; + } + exports.BracketMatch = BracketMatch; + +}); + +define("ace/edit_session", ["require", "exports", "module", "ace/lib/oop", "ace/lib/lang", "ace/bidihandler", "ace/config", "ace/lib/event_emitter", "ace/selection", "ace/mode/text", "ace/range", "ace/document", "ace/background_tokenizer", "ace/search_highlight", "ace/undomanager", "ace/edit_session/folding", "ace/edit_session/bracket_match"], function(require, exports, module) { + "use strict"; + var oop = require("./lib/oop"); + var lang = require("./lib/lang"); + var BidiHandler = require("./bidihandler").BidiHandler; + var config = require("./config"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var Selection = require("./selection").Selection; + var TextMode = require("./mode/text").Mode; + var Range = require("./range").Range; + var Document = require("./document").Document; + var BackgroundTokenizer = require("./background_tokenizer").BackgroundTokenizer; + var SearchHighlight = require("./search_highlight").SearchHighlight; + var UndoManager = require("./undomanager").UndoManager; + var EditSession = /** @class */ (function() { + function EditSession(text, mode) { + this.doc; + this.$breakpoints = []; + this.$decorations = []; + this.$frontMarkers = {}; + this.$backMarkers = {}; + this.$markerId = 1; + this.$undoSelect = true; + this.$foldData = []; + this.id = "session" + (++EditSession.$uid); + this.$foldData.toString = function() { + return this.join("\n"); + }; + this.bgTokenizer = new BackgroundTokenizer((new TextMode()).getTokenizer(), this); + var _self = this; + this.bgTokenizer.on("update", function(e) { + _self._signal("tokenizerUpdate", e); + }); + this.on("changeFold", this.onChangeFold.bind(this)); + this.$onChange = this.onChange.bind(this); + if (typeof text != "object" || !text.getLine) + text = new Document( /**@type{string}*/ (text)); + this.setDocument(text); + this.selection = new Selection(this); + this.$bidiHandler = new BidiHandler(this); + config.resetOptions(this); + this.setMode(mode); + config._signal("session", this); + this.destroyed = false; + } + EditSession.prototype.setDocument = function(doc) { + if (this.doc) + this.doc.off("change", this.$onChange); + this.doc = doc; + doc.on("change", this.$onChange, true); + this.bgTokenizer.setDocument(this.getDocument()); + this.resetCaches(); + }; + EditSession.prototype.getDocument = function() { + return this.doc; + }; + EditSession.prototype.$resetRowCache = function(docRow) { + if (!docRow) { + this.$docRowCache = []; + this.$screenRowCache = []; + return; + } + var l = this.$docRowCache.length; + var i = this.$getRowCacheIndex(this.$docRowCache, docRow) + 1; + if (l > i) { + this.$docRowCache.splice(i, l); + this.$screenRowCache.splice(i, l); + } + }; + EditSession.prototype.$getRowCacheIndex = function(cacheArray, val) { + var low = 0; + var hi = cacheArray.length - 1; + while (low <= hi) { + var mid = (low + hi) >> 1; + var c = cacheArray[mid]; + if (val > c) + low = mid + 1; + else if (val < c) + hi = mid - 1; + else + return mid; + } + return low - 1; + }; + EditSession.prototype.resetCaches = function() { + this.$modified = true; + this.$wrapData = []; + this.$rowLengthCache = []; + this.$resetRowCache(0); + if (!this.destroyed) + this.bgTokenizer.start(0); + }; + EditSession.prototype.onChangeFold = function(e) { + var fold = e.data; + this.$resetRowCache(fold.start.row); + }; + EditSession.prototype.onChange = function(delta) { + this.$modified = true; + this.$bidiHandler.onChange(delta); + this.$resetRowCache(delta.start.row); + var removedFolds = this.$updateInternalDataOnChange(delta); + if (!this.$fromUndo && this.$undoManager) { + if (removedFolds && removedFolds.length) { + this.$undoManager.add({ + action: "removeFolds", + folds: removedFolds + }, this.mergeUndoDeltas); + this.mergeUndoDeltas = true; + } + this.$undoManager.add(delta, this.mergeUndoDeltas); + this.mergeUndoDeltas = true; + this.$informUndoManager.schedule(); + } + this.bgTokenizer.$updateOnChange(delta); + this._signal("change", delta); + }; + EditSession.prototype.setValue = function(text) { + this.doc.setValue(text); + this.selection.moveTo(0, 0); + this.$resetRowCache(0); + this.setUndoManager(this.$undoManager); + this.getUndoManager().reset(); + }; + EditSession.fromJSON = function(session) { + if (typeof session == "string") + session = JSON.parse(session); + var undoManager = new UndoManager(); + undoManager.$undoStack = session.history.undo; + undoManager.$redoStack = session.history.redo; + undoManager.mark = session.history.mark; + undoManager.$rev = session.history.rev; + var editSession = new EditSession(session.value); + session.folds.forEach(function(fold) { + editSession.addFold("...", Range.fromPoints(fold.start, fold.end)); + }); + editSession.setAnnotations(session.annotations); + editSession.setBreakpoints(session.breakpoints); + editSession.setMode(session.mode); + editSession.setScrollLeft(session.scrollLeft); + editSession.setScrollTop(session.scrollTop); + editSession.setUndoManager(undoManager); + editSession.selection.fromJSON(session.selection); + return editSession; + }; + EditSession.prototype.toJSON = function() { + return { + annotations: this.$annotations, + breakpoints: this.$breakpoints, + folds: this.getAllFolds().map(function(fold) { + return fold.range; + }), + history: this.getUndoManager(), + mode: this.$mode.$id, + scrollLeft: this.$scrollLeft, + scrollTop: this.$scrollTop, + selection: this.selection.toJSON(), + value: this.doc.getValue() + }; + }; + EditSession.prototype.toString = function() { + return this.doc.getValue(); + }; + EditSession.prototype.getSelection = function() { + return this.selection; + }; + EditSession.prototype.getState = function(row) { + return this.bgTokenizer.getState(row); + }; + EditSession.prototype.getTokens = function(row) { + return this.bgTokenizer.getTokens(row); + }; + EditSession.prototype.getTokenAt = function(row, column) { + var tokens = this.bgTokenizer.getTokens(row); + var token, c = 0; + if (column == null) { + var i = tokens.length - 1; + c = this.getLine(row).length; + } else { + for (var i = 0; i < tokens.length; i++) { + c += tokens[i].value.length; + if (c >= column) + break; + } + } + token = tokens[i]; + if (!token) + return null; + token.index = i; + token.start = c - token.value.length; + return token; + }; + EditSession.prototype.setUndoManager = function(undoManager) { + this.$undoManager = undoManager; + if (this.$informUndoManager) + this.$informUndoManager.cancel(); + if (undoManager) { + var self = this; + undoManager.addSession(this); + this.$syncInformUndoManager = function() { + self.$informUndoManager.cancel(); + self.mergeUndoDeltas = false; + }; + this.$informUndoManager = lang.delayedCall(this.$syncInformUndoManager); + } else { + this.$syncInformUndoManager = function() {}; + } + }; + EditSession.prototype.markUndoGroup = function() { + if (this.$syncInformUndoManager) + this.$syncInformUndoManager(); + }; + EditSession.prototype.getUndoManager = function() { + return this.$undoManager || this.$defaultUndoManager; + }; + EditSession.prototype.getTabString = function() { + if (this.getUseSoftTabs()) { + return lang.stringRepeat(" ", this.getTabSize()); + } else { + return "\t"; + } + }; + EditSession.prototype.setUseSoftTabs = function(val) { + this.setOption("useSoftTabs", val); + }; + EditSession.prototype.getUseSoftTabs = function() { + return this.$useSoftTabs && !this.$mode.$indentWithTabs; + }; + EditSession.prototype.setTabSize = function(tabSize) { + this.setOption("tabSize", tabSize); + }; + EditSession.prototype.getTabSize = function() { + return this.$tabSize; + }; + EditSession.prototype.isTabStop = function(position) { + return this.$useSoftTabs && (position.column % this.$tabSize === 0); + }; + EditSession.prototype.setNavigateWithinSoftTabs = function(navigateWithinSoftTabs) { + this.setOption("navigateWithinSoftTabs", navigateWithinSoftTabs); + }; + EditSession.prototype.getNavigateWithinSoftTabs = function() { + return this.$navigateWithinSoftTabs; + }; + EditSession.prototype.setOverwrite = function(overwrite) { + this.setOption("overwrite", overwrite); + }; + EditSession.prototype.getOverwrite = function() { + return this.$overwrite; + }; + EditSession.prototype.toggleOverwrite = function() { + this.setOverwrite(!this.$overwrite); + }; + EditSession.prototype.addGutterDecoration = function(row, className) { + if (!this.$decorations[row]) + this.$decorations[row] = ""; + this.$decorations[row] += " " + className; + this._signal("changeBreakpoint", {}); + }; + EditSession.prototype.removeGutterDecoration = function(row, className) { + this.$decorations[row] = (this.$decorations[row] || "").replace(" " + className, ""); + this._signal("changeBreakpoint", {}); + }; + EditSession.prototype.getBreakpoints = function() { + return this.$breakpoints; + }; + EditSession.prototype.setBreakpoints = function(rows) { + this.$breakpoints = []; + for (var i = 0; i < rows.length; i++) { + this.$breakpoints[rows[i]] = "ace_breakpoint"; + } + this._signal("changeBreakpoint", {}); + }; + EditSession.prototype.clearBreakpoints = function() { + this.$breakpoints = []; + this._signal("changeBreakpoint", {}); + }; + EditSession.prototype.setBreakpoint = function(row, className) { + if (className === undefined) + className = "ace_breakpoint"; + if (className) + this.$breakpoints[row] = className; + else + delete this.$breakpoints[row]; + this._signal("changeBreakpoint", {}); + }; + EditSession.prototype.clearBreakpoint = function(row) { + delete this.$breakpoints[row]; + this._signal("changeBreakpoint", {}); + }; + EditSession.prototype.addMarker = function(range, clazz, type, inFront) { + var id = this.$markerId++; + var marker = { + range: range, + type: type || "line", + renderer: typeof type == "function" ? type : null, + clazz: clazz, + inFront: !!inFront, + id: id + }; + if (inFront) { + this.$frontMarkers[id] = marker; + this._signal("changeFrontMarker"); + } else { + this.$backMarkers[id] = marker; + this._signal("changeBackMarker"); + } + return id; + }; + EditSession.prototype.addDynamicMarker = function(marker, inFront) { + if (!marker.update) + return; + var id = this.$markerId++; + marker.id = id; + marker.inFront = !!inFront; + if (inFront) { + this.$frontMarkers[id] = marker; + this._signal("changeFrontMarker"); + } else { + this.$backMarkers[id] = marker; + this._signal("changeBackMarker"); + } + return marker; + }; + EditSession.prototype.removeMarker = function(markerId) { + var marker = this.$frontMarkers[markerId] || this.$backMarkers[markerId]; + if (!marker) + return; + var markers = marker.inFront ? this.$frontMarkers : this.$backMarkers; + delete(markers[markerId]); + this._signal(marker.inFront ? "changeFrontMarker" : "changeBackMarker"); + }; + EditSession.prototype.getMarkers = function(inFront) { + return inFront ? this.$frontMarkers : this.$backMarkers; + }; + EditSession.prototype.highlight = function(re) { + if (!this.$searchHighlight) { + var highlight = new SearchHighlight(null, "ace_selected-word", "text"); + this.$searchHighlight = this.addDynamicMarker(highlight); + } + this.$searchHighlight.setRegexp(re); + }; + EditSession.prototype.highlightLines = function(startRow, endRow, clazz, inFront) { + if (typeof endRow != "number") { + clazz = endRow; + endRow = startRow; + } + if (!clazz) + clazz = "ace_step"; + var range = new Range(startRow, 0, endRow, Infinity); + range.id = this.addMarker(range, clazz, "fullLine", inFront); + return range; + }; + EditSession.prototype.setAnnotations = function(annotations) { + this.$annotations = annotations; + this._signal("changeAnnotation", {}); + }; + EditSession.prototype.getAnnotations = function() { + return this.$annotations || []; + }; + EditSession.prototype.clearAnnotations = function() { + this.setAnnotations([]); + }; + EditSession.prototype.$detectNewLine = function(text) { + var match = text.match(/^.*?(\r?\n)/m); + if (match) { + this.$autoNewLine = match[1]; + } else { + this.$autoNewLine = "\n"; + } + }; + EditSession.prototype.getWordRange = function(row, column) { + var line = this.getLine(row); + var inToken = false; + if (column > 0) + inToken = !!line.charAt(column - 1).match(this.tokenRe); + if (!inToken) + inToken = !!line.charAt(column).match(this.tokenRe); + if (inToken) + var re = this.tokenRe; + else if (/^\s+$/.test(line.slice(column - 1, column + 1))) + var re = /\s/; + else + var re = this.nonTokenRe; + var start = column; + if (start > 0) { + do { + start--; + } while (start >= 0 && line.charAt(start).match(re)); + start++; + } + var end = column; + while (end < line.length && line.charAt(end).match(re)) { + end++; + } + return new Range(row, start, row, end); + }; + EditSession.prototype.getAWordRange = function(row, column) { + var wordRange = this.getWordRange(row, column); + var line = this.getLine(wordRange.end.row); + while (line.charAt(wordRange.end.column).match(/[ \t]/)) { + wordRange.end.column += 1; + } + return wordRange; + }; + EditSession.prototype.setNewLineMode = function(newLineMode) { + this.doc.setNewLineMode(newLineMode); + }; + EditSession.prototype.getNewLineMode = function() { + return this.doc.getNewLineMode(); + }; + EditSession.prototype.setUseWorker = function(useWorker) { + this.setOption("useWorker", useWorker); + }; + EditSession.prototype.getUseWorker = function() { + return this.$useWorker; + }; + EditSession.prototype.onReloadTokenizer = function(e) { + var rows = e.data; + this.bgTokenizer.start(rows.first); + this._signal("tokenizerUpdate", e); + }; + EditSession.prototype.setMode = function(mode, cb) { + if (mode && typeof mode === "object") { + if (mode.getTokenizer) + return this.$onChangeMode(mode); + var options = mode; + var path = options.path; + } else { + path = /**@type{string}*/ (mode) || "ace/mode/text"; + } + if (!this.$modes["ace/mode/text"]) + this.$modes["ace/mode/text"] = new TextMode(); + if (this.$modes[path] && !options) { + this.$onChangeMode(this.$modes[path]); + cb && cb(); + return; + } + this.$modeId = path; + config.loadModule(["mode", path], function(m) { + if (this.$modeId !== path) + return cb && cb(); + if (this.$modes[path] && !options) { + this.$onChangeMode(this.$modes[path]); + } else if (m && m.Mode) { + m = new m.Mode(options); + if (!options) { + this.$modes[path] = m; + m.$id = path; + } + this.$onChangeMode(m); + } + cb && cb(); + }.bind(this)); + if (!this.$mode) + this.$onChangeMode(this.$modes["ace/mode/text"], true); + }; + EditSession.prototype.$onChangeMode = function(mode, $isPlaceholder) { + if (!$isPlaceholder) + this.$modeId = mode.$id; + if (this.$mode === mode) + return; + var oldMode = this.$mode; + this.$mode = mode; + this.$stopWorker(); + if (this.$useWorker) + this.$startWorker(); + var tokenizer = mode.getTokenizer(); + if (tokenizer.on !== undefined) { + var onReloadTokenizer = this.onReloadTokenizer.bind(this); + tokenizer.on("update", onReloadTokenizer); + } + this.bgTokenizer.setTokenizer(tokenizer); + this.bgTokenizer.setDocument(this.getDocument()); + this.tokenRe = mode.tokenRe; + this.nonTokenRe = mode.nonTokenRe; + if (!$isPlaceholder) { + if (mode.attachToSession) + mode.attachToSession(this); + this.$options.wrapMethod.set.call(this, this.$wrapMethod); + this.$setFolding(mode.foldingRules); + this.bgTokenizer.start(0); + this._emit("changeMode", { + oldMode: oldMode, + mode: mode + }); + } + }; + EditSession.prototype.$stopWorker = function() { + if (this.$worker) { + this.$worker.terminate(); + this.$worker = null; + } + }; + EditSession.prototype.$startWorker = function() { + try { + this.$worker = this.$mode.createWorker(this); + } catch (e) { + config.warn("Could not load worker", e); + this.$worker = null; + } + }; + EditSession.prototype.getMode = function() { + return this.$mode; + }; + EditSession.prototype.setScrollTop = function(scrollTop) { + if (this.$scrollTop === scrollTop || isNaN(scrollTop)) + return; + this.$scrollTop = scrollTop; + this._signal("changeScrollTop", scrollTop); + }; + EditSession.prototype.getScrollTop = function() { + return this.$scrollTop; + }; + EditSession.prototype.setScrollLeft = function(scrollLeft) { + if (this.$scrollLeft === scrollLeft || isNaN(scrollLeft)) + return; + this.$scrollLeft = scrollLeft; + this._signal("changeScrollLeft", scrollLeft); + }; + EditSession.prototype.getScrollLeft = function() { + return this.$scrollLeft; + }; + EditSession.prototype.getScreenWidth = function() { + this.$computeWidth(); + if (this.lineWidgets) + return Math.max(this.getLineWidgetMaxWidth(), this.screenWidth); + return this.screenWidth; + }; + EditSession.prototype.getLineWidgetMaxWidth = function() { + if (this.lineWidgetsWidth != null) + return this.lineWidgetsWidth; + var width = 0; + this.lineWidgets.forEach(function(w) { + if (w && w.screenWidth > width) + width = w.screenWidth; + }); + return this.lineWidgetWidth = width; + }; + EditSession.prototype.$computeWidth = function(force) { + if (this.$modified || force) { + this.$modified = false; + if (this.$useWrapMode) + return this.screenWidth = this.$wrapLimit; + var lines = this.doc.getAllLines(); + var cache = this.$rowLengthCache; + var longestScreenLine = 0; + var foldIndex = 0; + var foldLine = this.$foldData[foldIndex]; + var foldStart = foldLine ? foldLine.start.row : Infinity; + var len = lines.length; + for (var i = 0; i < len; i++) { + if (i > foldStart) { + i = foldLine.end.row + 1; + if (i >= len) + break; + foldLine = this.$foldData[foldIndex++]; + foldStart = foldLine ? foldLine.start.row : Infinity; + } + if (cache[i] == null) + cache[i] = this.$getStringScreenWidth(lines[i])[0]; + if (cache[i] > longestScreenLine) + longestScreenLine = cache[i]; + } + this.screenWidth = longestScreenLine; + } + }; + EditSession.prototype.getLine = function(row) { + return this.doc.getLine(row); + }; + EditSession.prototype.getLines = function(firstRow, lastRow) { + return this.doc.getLines(firstRow, lastRow); + }; + EditSession.prototype.getLength = function() { + return this.doc.getLength(); + }; + EditSession.prototype.getTextRange = function(range) { + return this.doc.getTextRange(range || this.selection.getRange()); + }; + EditSession.prototype.insert = function(position, text) { + return this.doc.insert(position, text); + }; + EditSession.prototype.remove = function(range) { + return this.doc.remove(range); + }; + EditSession.prototype.removeFullLines = function(firstRow, lastRow) { + return this.doc.removeFullLines(firstRow, lastRow); + }; + EditSession.prototype.undoChanges = function(deltas, dontSelect) { + if (!deltas.length) + return; + this.$fromUndo = true; + for (var i = deltas.length - 1; i != -1; i--) { + var delta = deltas[i]; + if (delta.action == "insert" || delta.action == "remove") { + this.doc.revertDelta(delta); + } else if (delta.folds) { + this.addFolds(delta.folds); + } + } + if (!dontSelect && this.$undoSelect) { + if (deltas.selectionBefore) + this.selection.fromJSON(deltas.selectionBefore); + else + this.selection.setRange(this.$getUndoSelection(deltas, true)); + } + this.$fromUndo = false; + }; + EditSession.prototype.redoChanges = function(deltas, dontSelect) { + if (!deltas.length) + return; + this.$fromUndo = true; + for (var i = 0; i < deltas.length; i++) { + var delta = deltas[i]; + if (delta.action == "insert" || delta.action == "remove") { + this.doc.$safeApplyDelta(delta); + } + } + if (!dontSelect && this.$undoSelect) { + if (deltas.selectionAfter) + this.selection.fromJSON(deltas.selectionAfter); + else + this.selection.setRange(this.$getUndoSelection(deltas, false)); + } + this.$fromUndo = false; + }; + EditSession.prototype.setUndoSelect = function(enable) { + this.$undoSelect = enable; + }; + EditSession.prototype.$getUndoSelection = function(deltas, isUndo) { + function isInsert(delta) { + return isUndo ? delta.action !== "insert" : delta.action === "insert"; + } + var range, point; + for (var i = 0; i < deltas.length; i++) { + var delta = deltas[i]; + if (!delta.start) + continue; // skip folds + if (!range) { + if (isInsert(delta)) { + range = Range.fromPoints(delta.start, delta.end); + } else { + range = Range.fromPoints(delta.start, delta.start); + } + continue; + } + if (isInsert(delta)) { + point = delta.start; + if (range.compare(point.row, point.column) == -1) { + range.setStart(point); + } + point = delta.end; + if (range.compare(point.row, point.column) == 1) { + range.setEnd(point); + } + } else { + point = delta.start; + if (range.compare(point.row, point.column) == -1) { + range = Range.fromPoints(delta.start, delta.start); + } + } + } + return range; + }; + EditSession.prototype.replace = function(range, text) { + return this.doc.replace(range, text); + }; + EditSession.prototype.moveText = function(fromRange, toPosition, copy) { + var text = this.getTextRange(fromRange); + var folds = this.getFoldsInRange(fromRange); + var toRange = Range.fromPoints(toPosition, toPosition); + if (!copy) { + this.remove(fromRange); + var rowDiff = fromRange.start.row - fromRange.end.row; + var collDiff = rowDiff ? -fromRange.end.column : fromRange.start.column - fromRange.end.column; + if (collDiff) { + if (toRange.start.row == fromRange.end.row && toRange.start.column > fromRange.end.column) + toRange.start.column += collDiff; + if (toRange.end.row == fromRange.end.row && toRange.end.column > fromRange.end.column) + toRange.end.column += collDiff; + } + if (rowDiff && toRange.start.row >= fromRange.end.row) { + toRange.start.row += rowDiff; + toRange.end.row += rowDiff; + } + } + toRange.end = this.insert(toRange.start, text); + if (folds.length) { + var oldStart = fromRange.start; + var newStart = toRange.start; + var rowDiff = newStart.row - oldStart.row; + var collDiff = newStart.column - oldStart.column; + this.addFolds(folds.map(function(x) { + x = x.clone(); + if (x.start.row == oldStart.row) + x.start.column += collDiff; + if (x.end.row == oldStart.row) + x.end.column += collDiff; + x.start.row += rowDiff; + x.end.row += rowDiff; + return x; + })); + } + return toRange; + }; + EditSession.prototype.indentRows = function(startRow, endRow, indentString) { + indentString = indentString.replace(/\t/g, this.getTabString()); + for (var row = startRow; row <= endRow; row++) + this.doc.insertInLine({ + row: row, + column: 0 + }, indentString); + }; + EditSession.prototype.outdentRows = function(range) { + var rowRange = range.collapseRows(); + var deleteRange = new Range(0, 0, 0, 0); + var size = this.getTabSize(); + for (var i = rowRange.start.row; i <= rowRange.end.row; ++i) { + var line = this.getLine(i); + deleteRange.start.row = i; + deleteRange.end.row = i; + for (var j = 0; j < size; ++j) + if (line.charAt(j) != ' ') + break; + if (j < size && line.charAt(j) == '\t') { + deleteRange.start.column = j; + deleteRange.end.column = j + 1; + } else { + deleteRange.start.column = 0; + deleteRange.end.column = j; + } + this.remove(deleteRange); + } + }; + EditSession.prototype.$moveLines = function(firstRow, lastRow, dir) { + firstRow = this.getRowFoldStart(firstRow); + lastRow = this.getRowFoldEnd(lastRow); + if (dir < 0) { + var row = this.getRowFoldStart(firstRow + dir); + if (row < 0) + return 0; + var diff = row - firstRow; + } else if (dir > 0) { + var row = this.getRowFoldEnd(lastRow + dir); + if (row > this.doc.getLength() - 1) + return 0; + var diff = row - lastRow; + } else { + firstRow = this.$clipRowToDocument(firstRow); + lastRow = this.$clipRowToDocument(lastRow); + var diff = lastRow - firstRow + 1; + } + var range = new Range(firstRow, 0, lastRow, Number.MAX_VALUE); + var folds = this.getFoldsInRange(range).map(function(x) { + x = x.clone(); + x.start.row += diff; + x.end.row += diff; + return x; + }); + var lines = dir == 0 ? + this.doc.getLines(firstRow, lastRow) : + this.doc.removeFullLines(firstRow, lastRow); + this.doc.insertFullLines(firstRow + diff, lines); + folds.length && this.addFolds(folds); + return diff; + }; + EditSession.prototype.moveLinesUp = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, -1); + }; + EditSession.prototype.moveLinesDown = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, 1); + }; + EditSession.prototype.duplicateLines = function(firstRow, lastRow) { + return this.$moveLines(firstRow, lastRow, 0); + }; + EditSession.prototype.$clipRowToDocument = function(row) { + return Math.max(0, Math.min(row, this.doc.getLength() - 1)); + }; + EditSession.prototype.$clipColumnToRow = function(row, column) { + if (column < 0) + return 0; + return Math.min(this.doc.getLine(row).length, column); + }; + EditSession.prototype.$clipPositionToDocument = function(row, column) { + column = Math.max(0, column); + if (row < 0) { + row = 0; + column = 0; + } else { + var len = this.doc.getLength(); + if (row >= len) { + row = len - 1; + column = this.doc.getLine(len - 1).length; + } else { + column = Math.min(this.doc.getLine(row).length, column); + } + } + return { + row: row, + column: column + }; + }; + EditSession.prototype.$clipRangeToDocument = function(range) { + if (range.start.row < 0) { + range.start.row = 0; + range.start.column = 0; + } else { + range.start.column = this.$clipColumnToRow(range.start.row, range.start.column); + } + var len = this.doc.getLength() - 1; + if (range.end.row > len) { + range.end.row = len; + range.end.column = this.doc.getLine(len).length; + } else { + range.end.column = this.$clipColumnToRow(range.end.row, range.end.column); + } + return range; + }; + EditSession.prototype.setUseWrapMode = function(useWrapMode) { + if (useWrapMode != this.$useWrapMode) { + this.$useWrapMode = useWrapMode; + this.$modified = true; + this.$resetRowCache(0); + if (useWrapMode) { + var len = this.getLength(); + this.$wrapData = Array(len); + this.$updateWrapData(0, len - 1); + } + this._signal("changeWrapMode"); + } + }; + EditSession.prototype.getUseWrapMode = function() { + return this.$useWrapMode; + }; + EditSession.prototype.setWrapLimitRange = function(min, max) { + if (this.$wrapLimitRange.min !== min || this.$wrapLimitRange.max !== max) { + this.$wrapLimitRange = { + min: min, + max: max + }; + this.$modified = true; + this.$bidiHandler.markAsDirty(); + if (this.$useWrapMode) + this._signal("changeWrapMode"); + } + }; + EditSession.prototype.adjustWrapLimit = function(desiredLimit, $printMargin) { + var limits = this.$wrapLimitRange; + if (limits.max < 0) + limits = { + min: $printMargin, + max: $printMargin + }; + var wrapLimit = this.$constrainWrapLimit(desiredLimit, limits.min, limits.max); + if (wrapLimit != this.$wrapLimit && wrapLimit > 1) { + this.$wrapLimit = wrapLimit; + this.$modified = true; + if (this.$useWrapMode) { + this.$updateWrapData(0, this.getLength() - 1); + this.$resetRowCache(0); + this._signal("changeWrapLimit"); + } + return true; + } + return false; + }; + EditSession.prototype.$constrainWrapLimit = function(wrapLimit, min, max) { + if (min) + wrapLimit = Math.max(min, wrapLimit); + if (max) + wrapLimit = Math.min(max, wrapLimit); + return wrapLimit; + }; + EditSession.prototype.getWrapLimit = function() { + return this.$wrapLimit; + }; + EditSession.prototype.setWrapLimit = function(limit) { + this.setWrapLimitRange(limit, limit); + }; + EditSession.prototype.getWrapLimitRange = function() { + return { + min: this.$wrapLimitRange.min, + max: this.$wrapLimitRange.max + }; + }; + EditSession.prototype.$updateInternalDataOnChange = function(delta) { + var useWrapMode = this.$useWrapMode; + var action = delta.action; + var start = delta.start; + var end = delta.end; + var firstRow = start.row; + var lastRow = end.row; + var len = lastRow - firstRow; + var removedFolds = null; + this.$updating = true; + if (len != 0) { + if (action === "remove") { + this[useWrapMode ? "$wrapData" : "$rowLengthCache"].splice(firstRow, len); + var foldLines = this.$foldData; + removedFolds = this.getFoldsInRange(delta); + this.removeFolds(removedFolds); + var foldLine = this.getFoldLine(end.row); + var idx = 0; + if (foldLine) { + foldLine.addRemoveChars(end.row, end.column, start.column - end.column); + foldLine.shiftRow(-len); + var foldLineBefore = this.getFoldLine(firstRow); + if (foldLineBefore && foldLineBefore !== foldLine) { + foldLineBefore.merge(foldLine); + foldLine = foldLineBefore; + } + idx = foldLines.indexOf(foldLine) + 1; + } + for (idx; idx < foldLines.length; idx++) { + var foldLine = foldLines[idx]; + if (foldLine.start.row >= end.row) { + foldLine.shiftRow(-len); + } + } + lastRow = firstRow; + } else { + var args = Array(len); + args.unshift(firstRow, 0); + var arr = useWrapMode ? this.$wrapData : this.$rowLengthCache; + arr.splice.apply(arr, args); + var foldLines = this.$foldData; + var foldLine = this.getFoldLine(firstRow); + var idx = 0; + if (foldLine) { + var cmp = foldLine.range.compareInside(start.row, start.column); + if (cmp == 0) { + foldLine = foldLine.split(start.row, start.column); + if (foldLine) { + foldLine.shiftRow(len); + foldLine.addRemoveChars(lastRow, 0, end.column - start.column); + } + } else + if (cmp == -1) { + foldLine.addRemoveChars(firstRow, 0, end.column - start.column); + foldLine.shiftRow(len); + } + idx = foldLines.indexOf(foldLine) + 1; + } + for (idx; idx < foldLines.length; idx++) { + var foldLine = foldLines[idx]; + if (foldLine.start.row >= firstRow) { + foldLine.shiftRow(len); + } + } + } + } else { + len = Math.abs(delta.start.column - delta.end.column); + if (action === "remove") { + removedFolds = this.getFoldsInRange(delta); + this.removeFolds(removedFolds); + len = -len; + } + var foldLine = this.getFoldLine(firstRow); + if (foldLine) { + foldLine.addRemoveChars(firstRow, start.column, len); + } + } + if (useWrapMode && this.$wrapData.length != this.doc.getLength()) { + console.error("doc.getLength() and $wrapData.length have to be the same!"); + } + this.$updating = false; + if (useWrapMode) + this.$updateWrapData(firstRow, lastRow); + else + this.$updateRowLengthCache(firstRow, lastRow); + return removedFolds; + }; + EditSession.prototype.$updateRowLengthCache = function(firstRow, lastRow) { + this.$rowLengthCache[firstRow] = null; + this.$rowLengthCache[lastRow] = null; + }; + EditSession.prototype.$updateWrapData = function(firstRow, lastRow) { + var lines = this.doc.getAllLines(); + var tabSize = this.getTabSize(); + var wrapData = this.$wrapData; + var wrapLimit = this.$wrapLimit; + var tokens; + var foldLine; + var row = firstRow; + lastRow = Math.min(lastRow, lines.length - 1); + while (row <= lastRow) { + foldLine = this.getFoldLine(row, foldLine); + if (!foldLine) { + tokens = this.$getDisplayTokens(lines[row]); + wrapData[row] = this.$computeWrapSplits(tokens, wrapLimit, tabSize); + row++; + } else { + tokens = []; + foldLine.walk(function(placeholder, row, column, lastColumn) { + var walkTokens; + if (placeholder != null) { + walkTokens = this.$getDisplayTokens(placeholder, tokens.length); + walkTokens[0] = PLACEHOLDER_START; + for (var i = 1; i < walkTokens.length; i++) { + walkTokens[i] = PLACEHOLDER_BODY; + } + } else { + walkTokens = this.$getDisplayTokens(lines[row].substring(lastColumn, column), tokens.length); + } + tokens = tokens.concat(walkTokens); + }.bind(this), foldLine.end.row, lines[foldLine.end.row].length + 1); + wrapData[foldLine.start.row] = this.$computeWrapSplits(tokens, wrapLimit, tabSize); + row = foldLine.end.row + 1; + } + } + }; + EditSession.prototype.$computeWrapSplits = function(tokens, wrapLimit, tabSize) { + if (tokens.length == 0) { + return []; + } + var splits = []; + var displayLength = tokens.length; + var lastSplit = 0, + lastDocSplit = 0; + var isCode = this.$wrapAsCode; + var indentedSoftWrap = this.$indentedSoftWrap; + var maxIndent = wrapLimit <= Math.max(2 * tabSize, 8) || + indentedSoftWrap === false ? 0 : Math.floor(wrapLimit / 2); + + function getWrapIndent() { + var indentation = 0; + if (maxIndent === 0) + return indentation; + if (indentedSoftWrap) { + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + if (token == SPACE) + indentation += 1; + else if (token == TAB) + indentation += tabSize; + else if (token == TAB_SPACE) + continue; + else + break; + } + } + if (isCode && indentedSoftWrap !== false) + indentation += tabSize; + return Math.min(indentation, maxIndent); + } + + function addSplit(screenPos) { + var len = screenPos - lastSplit; + for (var i = lastSplit; i < screenPos; i++) { + var ch = tokens[i]; + if (ch === 12 || ch === 2) + len -= 1; + } + if (!splits.length) { + indent = getWrapIndent(); + splits.indent = indent; + } + lastDocSplit += len; + splits.push(lastDocSplit); + lastSplit = screenPos; + } + var indent = 0; + while (displayLength - lastSplit > wrapLimit - indent) { + var split = lastSplit + wrapLimit - indent; + if (tokens[split - 1] >= SPACE && tokens[split] >= SPACE) { + addSplit(split); + continue; + } + if (tokens[split] == PLACEHOLDER_START || tokens[split] == PLACEHOLDER_BODY) { + for (split; split != lastSplit - 1; split--) { + if (tokens[split] == PLACEHOLDER_START) { + break; + } + } + if (split > lastSplit) { + addSplit(split); + continue; + } + split = lastSplit + wrapLimit; + for (split; split < tokens.length; split++) { + if (tokens[split] != PLACEHOLDER_BODY) { + break; + } + } + if (split == tokens.length) { + break; // Breaks the while-loop. + } + addSplit(split); + continue; + } + var minSplit = Math.max(split - (wrapLimit - (wrapLimit >> 2)), lastSplit - 1); + while (split > minSplit && tokens[split] < PLACEHOLDER_START) { + split--; + } + if (isCode) { + while (split > minSplit && tokens[split] < PLACEHOLDER_START) { + split--; + } + while (split > minSplit && tokens[split] == PUNCTUATION) { + split--; + } + } else { + while (split > minSplit && tokens[split] < SPACE) { + split--; + } + } + if (split > minSplit) { + addSplit(++split); + continue; + } + split = lastSplit + wrapLimit; + if (tokens[split] == CHAR_EXT) + split--; + addSplit(split - indent); + } + return splits; + }; + EditSession.prototype.$getDisplayTokens = function(str, offset) { + var arr = []; + var tabSize; + offset = offset || 0; + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + if (c == 9) { + tabSize = this.getScreenTabSize(arr.length + offset); + arr.push(TAB); + for (var n = 1; n < tabSize; n++) { + arr.push(TAB_SPACE); + } + } else if (c == 32) { + arr.push(SPACE); + } else if ((c > 39 && c < 48) || (c > 57 && c < 64)) { + arr.push(PUNCTUATION); + } else if (c >= 0x1100 && isFullWidth(c)) { + arr.push(CHAR, CHAR_EXT); + } else { + arr.push(CHAR); + } + } + return arr; + }; + EditSession.prototype.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) { + if (maxScreenColumn == 0) + return [0, 0]; + if (maxScreenColumn == null) + maxScreenColumn = Infinity; + screenColumn = screenColumn || 0; + var c, column; + for (column = 0; column < str.length; column++) { + c = str.charCodeAt(column); + if (c == 9) { + screenColumn += this.getScreenTabSize(screenColumn); + } else if (c >= 0x1100 && isFullWidth(c)) { + screenColumn += 2; + } else { + screenColumn += 1; + } + if (screenColumn > maxScreenColumn) { + break; + } + } + return [screenColumn, column]; + }; + EditSession.prototype.getRowLength = function(row) { + var h = 1; + if (this.lineWidgets) + h += this.lineWidgets[row] && this.lineWidgets[row].rowCount || 0; + if (!this.$useWrapMode || !this.$wrapData[row]) + return h; + else + return this.$wrapData[row].length + h; + }; + EditSession.prototype.getRowLineCount = function(row) { + if (!this.$useWrapMode || !this.$wrapData[row]) { + return 1; + } else { + return this.$wrapData[row].length + 1; + } + }; + EditSession.prototype.getRowWrapIndent = function(screenRow) { + if (this.$useWrapMode) { + var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE); + var splits = this.$wrapData[pos.row]; + return splits.length && splits[0] < pos.column ? splits.indent : 0; + } else { + return 0; + } + }; + EditSession.prototype.getScreenLastRowColumn = function(screenRow) { + var pos = this.screenToDocumentPosition(screenRow, Number.MAX_VALUE); + return this.documentToScreenColumn(pos.row, pos.column); + }; + EditSession.prototype.getDocumentLastRowColumn = function(docRow, docColumn) { + var screenRow = this.documentToScreenRow(docRow, docColumn); + return this.getScreenLastRowColumn(screenRow); + }; + EditSession.prototype.getDocumentLastRowColumnPosition = function(docRow, docColumn) { + var screenRow = this.documentToScreenRow(docRow, docColumn); + return this.screenToDocumentPosition(screenRow, Number.MAX_VALUE / 10); + }; + EditSession.prototype.getRowSplitData = function(row) { + if (!this.$useWrapMode) { + return undefined; + } else { + return this.$wrapData[row]; + } + }; + EditSession.prototype.getScreenTabSize = function(screenColumn) { + return this.$tabSize - (screenColumn % this.$tabSize | 0); + }; + EditSession.prototype.screenToDocumentRow = function(screenRow, screenColumn) { + return this.screenToDocumentPosition(screenRow, screenColumn).row; + }; + EditSession.prototype.screenToDocumentColumn = function(screenRow, screenColumn) { + return this.screenToDocumentPosition(screenRow, screenColumn).column; + }; + EditSession.prototype.screenToDocumentPosition = function(screenRow, screenColumn, offsetX) { + if (screenRow < 0) + return { + row: 0, + column: 0 + }; + var line; + var docRow = 0; + var docColumn = 0; + var column; + var row = 0; + var rowLength = 0; + var rowCache = this.$screenRowCache; + var i = this.$getRowCacheIndex(rowCache, screenRow); + var l = rowCache.length; + if (l && i >= 0) { + var row = rowCache[i]; + var docRow = this.$docRowCache[i]; + var doCache = screenRow > rowCache[l - 1]; + } else { + var doCache = !l; + } + var maxRow = this.getLength() - 1; + var foldLine = this.getNextFoldLine(docRow); + var foldStart = foldLine ? foldLine.start.row : Infinity; + while (row <= screenRow) { + rowLength = this.getRowLength(docRow); + if (row + rowLength > screenRow || docRow >= maxRow) { + break; + } else { + row += rowLength; + docRow++; + if (docRow > foldStart) { + docRow = foldLine.end.row + 1; + foldLine = this.getNextFoldLine(docRow, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + } + if (doCache) { + this.$docRowCache.push(docRow); + this.$screenRowCache.push(row); + } + } + if (foldLine && foldLine.start.row <= docRow) { + line = this.getFoldDisplayLine(foldLine); + docRow = foldLine.start.row; + } else if (row + rowLength <= screenRow || docRow > maxRow) { + return { + row: maxRow, + column: this.getLine(maxRow).length + }; + } else { + line = this.getLine(docRow); + foldLine = null; + } + var wrapIndent = 0, + splitIndex = Math.floor(screenRow - row); + if (this.$useWrapMode) { + var splits = this.$wrapData[docRow]; + if (splits) { + column = splits[splitIndex]; + if (splitIndex > 0 && splits.length) { + wrapIndent = splits.indent; + docColumn = splits[splitIndex - 1] || splits[splits.length - 1]; + line = line.substring(docColumn); + } + } + } + if (offsetX !== undefined && this.$bidiHandler.isBidiRow(row + splitIndex, docRow, splitIndex)) + screenColumn = this.$bidiHandler.offsetToCol(offsetX); + docColumn += this.$getStringScreenWidth(line, screenColumn - wrapIndent)[1]; + if (this.$useWrapMode && docColumn >= column) + docColumn = column - 1; + if (foldLine) + return foldLine.idxToPosition(docColumn); + return { + row: docRow, + column: docColumn + }; + }; + EditSession.prototype.documentToScreenPosition = function(docRow, docColumn) { + if (typeof docColumn === "undefined") + var pos = this.$clipPositionToDocument( /**@type{Point}*/ (docRow).row, /**@type{Point}*/ (docRow).column); + else + pos = this.$clipPositionToDocument( /**@type{number}*/ (docRow), docColumn); + docRow = pos.row; + docColumn = pos.column; + var screenRow = 0; + var foldStartRow = null; + var fold = null; + fold = this.getFoldAt(docRow, docColumn, 1); + if (fold) { + docRow = fold.start.row; + docColumn = fold.start.column; + } + var rowEnd, row = 0; + var rowCache = this.$docRowCache; + var i = this.$getRowCacheIndex(rowCache, docRow); + var l = rowCache.length; + if (l && i >= 0) { + var row = rowCache[i]; + var screenRow = this.$screenRowCache[i]; + var doCache = docRow > rowCache[l - 1]; + } else { + var doCache = !l; + } + var foldLine = this.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + while (row < docRow) { + if (row >= foldStart) { + rowEnd = foldLine.end.row + 1; + if (rowEnd > docRow) + break; + foldLine = this.getNextFoldLine(rowEnd, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } else { + rowEnd = row + 1; + } + screenRow += this.getRowLength(row); + row = rowEnd; + if (doCache) { + this.$docRowCache.push(row); + this.$screenRowCache.push(screenRow); + } + } + var textLine = ""; + if (foldLine && row >= foldStart) { + textLine = this.getFoldDisplayLine(foldLine, docRow, docColumn); + foldStartRow = foldLine.start.row; + } else { + textLine = this.getLine(docRow).substring(0, docColumn); + foldStartRow = docRow; + } + var wrapIndent = 0; + if (this.$useWrapMode) { + var wrapRow = this.$wrapData[foldStartRow]; + if (wrapRow) { + var screenRowOffset = 0; + while (textLine.length >= wrapRow[screenRowOffset]) { + screenRow++; + screenRowOffset++; + } + textLine = textLine.substring(wrapRow[screenRowOffset - 1] || 0, textLine.length); + wrapIndent = screenRowOffset > 0 ? wrapRow.indent : 0; + } + } + if (this.lineWidgets && this.lineWidgets[row] && this.lineWidgets[row].rowsAbove) + screenRow += this.lineWidgets[row].rowsAbove; + return { + row: screenRow, + column: wrapIndent + this.$getStringScreenWidth(textLine)[0] + }; + }; + EditSession.prototype.documentToScreenColumn = function(row, docColumn) { + return this.documentToScreenPosition(row, docColumn).column; + }; + EditSession.prototype.documentToScreenRow = function(docRow, docColumn) { + return this.documentToScreenPosition(docRow, docColumn).row; + }; + EditSession.prototype.getScreenLength = function() { + var screenRows = 0; + var fold = null; + if (!this.$useWrapMode) { + screenRows = this.getLength(); + var foldData = this.$foldData; + for (var i = 0; i < foldData.length; i++) { + fold = foldData[i]; + screenRows -= fold.end.row - fold.start.row; + } + } else { + var lastRow = this.$wrapData.length; + var row = 0, + i = 0; + var fold = this.$foldData[i++]; + var foldStart = fold ? fold.start.row : Infinity; + while (row < lastRow) { + var splits = this.$wrapData[row]; + screenRows += splits ? splits.length + 1 : 1; + row++; + if (row > foldStart) { + row = fold.end.row + 1; + fold = this.$foldData[i++]; + foldStart = fold ? fold.start.row : Infinity; + } + } + } + if (this.lineWidgets) + screenRows += this.$getWidgetScreenLength(); + return screenRows; + }; + EditSession.prototype.$setFontMetrics = function(fm) { + if (!this.$enableVarChar) + return; + this.$getStringScreenWidth = function(str, maxScreenColumn, screenColumn) { + if (maxScreenColumn === 0) + return [0, 0]; + if (!maxScreenColumn) + maxScreenColumn = Infinity; + screenColumn = screenColumn || 0; + var c, column; + for (column = 0; column < str.length; column++) { + c = str.charAt(column); + if (c === "\t") { + screenColumn += this.getScreenTabSize(screenColumn); + } else { + screenColumn += fm.getCharacterWidth(c); + } + if (screenColumn > maxScreenColumn) { + break; + } + } + return [screenColumn, column]; + }; + }; + EditSession.prototype.getPrecedingCharacter = function() { + var pos = this.selection.getCursor(); + if (pos.column === 0) { + return pos.row === 0 ? "" : this.doc.getNewLineCharacter(); + } + var currentLine = this.getLine(pos.row); + return currentLine[pos.column - 1]; + }; + EditSession.prototype.destroy = function() { + if (!this.destroyed) { + this.bgTokenizer.setDocument(null); + this.bgTokenizer.cleanup(); + this.destroyed = true; + } + this.$stopWorker(); + this.removeAllListeners(); + if (this.doc) { + this.doc.off("change", this.$onChange); + } + this.selection.detach(); + }; + return EditSession; + }()); + EditSession.$uid = 0; + EditSession.prototype.$modes = config.$modes; + EditSession.prototype.getValue = EditSession.prototype.toString; + EditSession.prototype.$defaultUndoManager = { + undo: function() {}, + redo: function() {}, + hasUndo: function() {}, + hasRedo: function() {}, + reset: function() {}, + add: function() {}, + addSelection: function() {}, + startNewGroup: function() {}, + addSession: function() {} + }; + EditSession.prototype.$overwrite = false; + EditSession.prototype.$mode = null; + EditSession.prototype.$modeId = null; + EditSession.prototype.$scrollTop = 0; + EditSession.prototype.$scrollLeft = 0; + EditSession.prototype.$wrapLimit = 80; + EditSession.prototype.$useWrapMode = false; + EditSession.prototype.$wrapLimitRange = { + min: null, + max: null + }; + EditSession.prototype.lineWidgets = null; + EditSession.prototype.isFullWidth = isFullWidth; + oop.implement(EditSession.prototype, EventEmitter); + var CHAR = 1, + CHAR_EXT = 2, + PLACEHOLDER_START = 3, + PLACEHOLDER_BODY = 4, + PUNCTUATION = 9, + SPACE = 10, + TAB = 11, + TAB_SPACE = 12; + + function isFullWidth(c) { + if (c < 0x1100) + return false; + return c >= 0x1100 && c <= 0x115F || + c >= 0x11A3 && c <= 0x11A7 || + c >= 0x11FA && c <= 0x11FF || + c >= 0x2329 && c <= 0x232A || + c >= 0x2E80 && c <= 0x2E99 || + c >= 0x2E9B && c <= 0x2EF3 || + c >= 0x2F00 && c <= 0x2FD5 || + c >= 0x2FF0 && c <= 0x2FFB || + c >= 0x3000 && c <= 0x303E || + c >= 0x3041 && c <= 0x3096 || + c >= 0x3099 && c <= 0x30FF || + c >= 0x3105 && c <= 0x312D || + c >= 0x3131 && c <= 0x318E || + c >= 0x3190 && c <= 0x31BA || + c >= 0x31C0 && c <= 0x31E3 || + c >= 0x31F0 && c <= 0x321E || + c >= 0x3220 && c <= 0x3247 || + c >= 0x3250 && c <= 0x32FE || + c >= 0x3300 && c <= 0x4DBF || + c >= 0x4E00 && c <= 0xA48C || + c >= 0xA490 && c <= 0xA4C6 || + c >= 0xA960 && c <= 0xA97C || + c >= 0xAC00 && c <= 0xD7A3 || + c >= 0xD7B0 && c <= 0xD7C6 || + c >= 0xD7CB && c <= 0xD7FB || + c >= 0xF900 && c <= 0xFAFF || + c >= 0xFE10 && c <= 0xFE19 || + c >= 0xFE30 && c <= 0xFE52 || + c >= 0xFE54 && c <= 0xFE66 || + c >= 0xFE68 && c <= 0xFE6B || + c >= 0xFF01 && c <= 0xFF60 || + c >= 0xFFE0 && c <= 0xFFE6; + } + require("./edit_session/folding").Folding.call(EditSession.prototype); + require("./edit_session/bracket_match").BracketMatch.call(EditSession.prototype); + config.defineOptions(EditSession.prototype, "session", { + wrap: { + set: function(value) { + if (!value || value == "off") + value = false; + else if (value == "free") + value = true; + else if (value == "printMargin") + value = -1; + else if (typeof value == "string") + value = parseInt(value, 10) || false; + if (this.$wrap == value) + return; + this.$wrap = value; + if (!value) { + this.setUseWrapMode(false); + } else { + var col = typeof value == "number" ? value : null; + this.setWrapLimitRange(col, col); + this.setUseWrapMode(true); + } + }, + get: function() { + if (this.getUseWrapMode()) { + if (this.$wrap == -1) + return "printMargin"; + if (!this.getWrapLimitRange().min) + return "free"; + return this.$wrap; + } + return "off"; + }, + handlesSet: true + }, + wrapMethod: { + set: function(val) { + val = val == "auto" ? + this.$mode.type != "text" : + val != "text"; + if (val != this.$wrapAsCode) { + this.$wrapAsCode = val; + if (this.$useWrapMode) { + this.$useWrapMode = false; + this.setUseWrapMode(true); + } + } + }, + initialValue: "auto" + }, + indentedSoftWrap: { + set: function() { + if (this.$useWrapMode) { + this.$useWrapMode = false; + this.setUseWrapMode(true); + } + }, + initialValue: true + }, + firstLineNumber: { + set: function() { + this._signal("changeBreakpoint"); + }, + initialValue: 1 + }, + useWorker: { + set: function(useWorker) { + this.$useWorker = useWorker; + this.$stopWorker(); + if (useWorker) + this.$startWorker(); + }, + initialValue: true + }, + useSoftTabs: { + initialValue: true + }, + tabSize: { + set: function(tabSize) { + tabSize = parseInt(tabSize); + if (tabSize > 0 && this.$tabSize !== tabSize) { + this.$modified = true; + this.$rowLengthCache = []; + this.$tabSize = tabSize; + this._signal("changeTabSize"); + } + }, + initialValue: 4, + handlesSet: true + }, + navigateWithinSoftTabs: { + initialValue: false + }, + foldStyle: { + set: function(val) { + this.setFoldStyle(val); + }, + handlesSet: true + }, + overwrite: { + set: function(val) { + this._signal("changeOverwrite"); + }, + initialValue: false + }, + newLineMode: { + set: function(val) { + this.doc.setNewLineMode(val); + }, + get: function() { + return this.doc.getNewLineMode(); + }, + handlesSet: true + }, + mode: { + set: function(val) { + this.setMode(val); + }, + get: function() { + return this.$modeId; + }, + handlesSet: true + } + }); + exports.EditSession = EditSession; + +}); + +define("ace/search", ["require", "exports", "module", "ace/lib/lang", "ace/lib/oop", "ace/range"], function(require, exports, module) { + "use strict"; + var lang = require("./lib/lang"); + var oop = require("./lib/oop"); + var Range = require("./range").Range; + var Search = /** @class */ (function() { + function Search() { + this.$options = {}; + } + Search.prototype.set = function(options) { + oop.mixin(this.$options, options); + return this; + }; + Search.prototype.getOptions = function() { + return lang.copyObject(this.$options); + }; + Search.prototype.setOptions = function(options) { + this.$options = options; + }; + Search.prototype.find = function(session) { + var options = this.$options; + var iterator = this.$matchIterator(session, options); + if (!iterator) + return false; + var firstRange = null; + iterator.forEach(function(sr, sc, er, ec) { + firstRange = new Range(sr, sc, er, ec); + if (sc == ec && options.start && /**@type{Range}*/ (options.start).start && + options.skipCurrent != false && firstRange.isEqual( /**@type{Range}*/ (options.start))) { + firstRange = null; + return false; + } + return true; + }); + return firstRange; + }; + Search.prototype.findAll = function(session) { + var options = this.$options; + if (!options.needle) + return []; + this.$assembleRegExp(options); + var range = options.range; + var lines = range ? + session.getLines(range.start.row, range.end.row) : + session.doc.getAllLines(); + var ranges = []; + var re = options.re; + if (options.$isMultiLine) { + var len = re.length; + var maxRow = lines.length - len; + var prevRange; + outer: for (var row = re.offset || 0; row <= maxRow; row++) { + for (var j = 0; j < len; j++) + if (lines[row + j].search(re[j]) == -1) + continue outer; + var startLine = lines[row]; + var line = lines[row + len - 1]; + var startIndex = startLine.length - startLine.match(re[0])[0].length; + var endIndex = line.match(re[len - 1])[0].length; + if (prevRange && prevRange.end.row === row && + prevRange.end.column > startIndex) { + continue; + } + ranges.push(prevRange = new Range(row, startIndex, row + len - 1, endIndex)); + if (len > 2) + row = row + len - 2; + } + } else { + for (var i = 0; i < lines.length; i++) { + var matches = lang.getMatchOffsets(lines[i], re); + for (var j = 0; j < matches.length; j++) { + var match = matches[j]; + ranges.push(new Range(i, match.offset, i, match.offset + match.length)); + } + } + } + if (range) { + var startColumn = range.start.column; + var endColumn = range.end.column; + var i = 0, + j = ranges.length - 1; + while (i < j && ranges[i].start.column < startColumn && ranges[i].start.row == 0) + i++; + var endRow = range.end.row - range.start.row; + while (i < j && ranges[j].end.column > endColumn && ranges[j].end.row == endRow) + j--; + ranges = ranges.slice(i, j + 1); + for (i = 0, j = ranges.length; i < j; i++) { + ranges[i].start.row += range.start.row; + ranges[i].end.row += range.start.row; + } + } + return ranges; + }; + Search.prototype.replace = function(input, replacement) { + var options = this.$options; + var re = this.$assembleRegExp(options); + if (options.$isMultiLine) + return replacement; + if (!re) + return; + var match = re.exec(input); + if (!match || match[0].length != input.length) + return null; + if (!options.regExp) { + replacement = replacement.replace(/\$/g, "$$$$"); + } + replacement = input.replace(re, replacement); + if (options.preserveCase) { + replacement = replacement.split(""); + for (var i = Math.min(input.length, input.length); i--;) { + var ch = input[i]; + if (ch && ch.toLowerCase() != ch) + replacement[i] = replacement[i].toUpperCase(); + else + replacement[i] = replacement[i].toLowerCase(); + } + replacement = replacement.join(""); + } + return replacement; + }; + Search.prototype.$assembleRegExp = function(options, $disableFakeMultiline) { + if (options.needle instanceof RegExp) + return options.re = options.needle; + var needle = options.needle; + if (!options.needle) + return options.re = false; + if (!options.regExp) + needle = lang.escapeRegExp(needle); + var modifier = options.caseSensitive ? "gm" : "gmi"; + try { + new RegExp(needle, "u"); + options.$supportsUnicodeFlag = true; + modifier += "u"; + } catch (e) { + options.$supportsUnicodeFlag = false; //left for backward compatibility with previous versions for cases like /ab\{2}/gu + } + if (options.wholeWord) + needle = addWordBoundary(needle, options); + options.$isMultiLine = !$disableFakeMultiline && /[\n\r]/.test(needle); + if (options.$isMultiLine) + return options.re = this.$assembleMultilineRegExp(needle, modifier); + try { + var re = new RegExp(needle, modifier); + } catch (e) { + re = false; + } + return options.re = re; + }; + Search.prototype.$assembleMultilineRegExp = function(needle, modifier) { + var parts = needle.replace(/\r\n|\r|\n/g, "$\n^").split("\n"); + var re = []; + for (var i = 0; i < parts.length; i++) + try { + re.push(new RegExp(parts[i], modifier)); + } + catch (e) { + return false; + } + return re; + }; + Search.prototype.$matchIterator = function(session, options) { + var re = this.$assembleRegExp(options); + if (!re) + return false; + var backwards = options.backwards == true; + var skipCurrent = options.skipCurrent != false; + var supportsUnicodeFlag = re.unicode; + var range = options.range; + var start = options.start; + if (!start) + start = range ? range[backwards ? "end" : "start"] : session.selection.getRange(); + if (start.start) + start = start[skipCurrent != backwards ? "end" : "start"]; + var firstRow = range ? range.start.row : 0; + var lastRow = range ? range.end.row : session.getLength() - 1; + if (backwards) { + var forEach = function(callback) { + var row = start.row; + if (forEachInLine(row, start.column, callback)) + return; + for (row--; row >= firstRow; row--) + if (forEachInLine(row, Number.MAX_VALUE, callback)) + return; + if (options.wrap == false) + return; + for (row = lastRow, firstRow = start.row; row >= firstRow; row--) + if (forEachInLine(row, Number.MAX_VALUE, callback)) + return; + }; + } else { + var forEach = function(callback) { + var row = start.row; + if (forEachInLine(row, start.column, callback)) + return; + for (row = row + 1; row <= lastRow; row++) + if (forEachInLine(row, 0, callback)) + return; + if (options.wrap == false) + return; + for (row = firstRow, lastRow = start.row; row <= lastRow; row++) + if (forEachInLine(row, 0, callback)) + return; + }; + } + if (options.$isMultiLine) { + var len = re.length; + var forEachInLine = function(row, offset, callback) { + var startRow = backwards ? row - len + 1 : row; + if (startRow < 0 || startRow + len > session.getLength()) + return; + var line = session.getLine(startRow); + var startIndex = line.search(re[0]); + if (!backwards && startIndex < offset || startIndex === -1) + return; + for (var i = 1; i < len; i++) { + line = session.getLine(startRow + i); + if (line.search(re[i]) == -1) + return; + } + var endIndex = line.match(re[len - 1])[0].length; + if (backwards && endIndex > offset) + return; + if (callback(startRow, startIndex, startRow + len - 1, endIndex)) + return true; + }; + } else if (backwards) { + var forEachInLine = function(row, endIndex, callback) { + var line = session.getLine(row); + var matches = []; + var m, last = 0; + re.lastIndex = 0; + while ((m = re.exec(line))) { + var length = m[0].length; + last = m.index; + if (!length) { + if (last >= line.length) + break; + re.lastIndex = last += lang.skipEmptyMatch(line, last, supportsUnicodeFlag); + } + if (m.index + length > endIndex) + break; + matches.push(m.index, length); + } + for (var i = matches.length - 1; i >= 0; i -= 2) { + var column = matches[i - 1]; + var length = matches[i]; + if (callback(row, column, row, column + length)) + return true; + } + }; + } else { + var forEachInLine = function(row, startIndex, callback) { + var line = session.getLine(row); + var last; + var m; + re.lastIndex = startIndex; + while ((m = re.exec(line))) { + var length = m[0].length; + last = m.index; + if (callback(row, last, row, last + length)) + return true; + if (!length) { + re.lastIndex = last += lang.skipEmptyMatch(line, last, supportsUnicodeFlag); + if (last >= line.length) + return false; + } + } + }; + } + return { + forEach: forEach + }; + }; + return Search; + }()); + + function addWordBoundary(needle, options) { + var supportsLookbehind = lang.supportsLookbehind(); + + function wordBoundary(c, firstChar) { + if (firstChar === void 0) { + firstChar = true; + } + var wordRegExp = supportsLookbehind && options.$supportsUnicodeFlag ? new RegExp("[\\p{L}\\p{N}_]", "u") : new RegExp("\\w"); + if (wordRegExp.test(c) || options.regExp) { + if (supportsLookbehind && options.$supportsUnicodeFlag) { + if (firstChar) + return "(?<=^|[^\\p{L}\\p{N}_])"; + return "(?=[^\\p{L}\\p{N}_]|$)"; + } + return "\\b"; + } + return ""; + } + var needleArray = Array.from(needle); + var firstChar = needleArray[0]; + var lastChar = needleArray[needleArray.length - 1]; + return wordBoundary(firstChar) + needle + wordBoundary(lastChar, false); + } + exports.Search = Search; + +}); + +define("ace/keyboard/hash_handler", ["require", "exports", "module", "ace/lib/keys", "ace/lib/useragent"], function(require, exports, module) { + "use strict"; + var __extends = (this && this.__extends) || (function() { + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ + __proto__: [] + } + instanceof Array && function(d, b) { + d.__proto__ = b; + }) || + function(d, b) { + for (var p in b) + if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); + }; + return function(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + var keyUtil = require("../lib/keys"); + var useragent = require("../lib/useragent"); + var KEY_MODS = keyUtil.KEY_MODS; + var MultiHashHandler = /** @class */ (function() { + function MultiHashHandler(config, platform) { + this.$init(config, platform, false); + } + MultiHashHandler.prototype.$init = function(config, platform, $singleCommand) { + this.platform = platform || (useragent.isMac ? "mac" : "win"); + this.commands = {}; + this.commandKeyBinding = {}; + this.addCommands(config); + this.$singleCommand = $singleCommand; + }; + MultiHashHandler.prototype.addCommand = function(command) { + if (this.commands[command.name]) + this.removeCommand(command); + this.commands[command.name] = command; + if (command.bindKey) + this._buildKeyHash(command); + }; + MultiHashHandler.prototype.removeCommand = function(command, keepCommand) { + var name = command && (typeof command === 'string' ? command : command.name); + command = this.commands[name]; + if (!keepCommand) + delete this.commands[name]; + var ckb = this.commandKeyBinding; + for (var keyId in ckb) { + var cmdGroup = ckb[keyId]; + if (cmdGroup == command) { + delete ckb[keyId]; + } else if (Array.isArray(cmdGroup)) { + var i = cmdGroup.indexOf(command); + if (i != -1) { + cmdGroup.splice(i, 1); + if (cmdGroup.length == 1) + ckb[keyId] = cmdGroup[0]; + } + } + } + }; + MultiHashHandler.prototype.bindKey = function(key, command, position) { + if (typeof key == "object" && key) { + if (position == undefined) + position = key.position; + key = key[this.platform]; + } + if (!key) + return; + if (typeof command == "function") + return this.addCommand({ + exec: command, + bindKey: key, + name: command.name || /**@type{string}*/ (key) + }); + (key).split("|").forEach(function(keyPart) { + var chain = ""; + if (keyPart.indexOf(" ") != -1) { + var parts = keyPart.split(/\s+/); + keyPart = parts.pop(); + parts.forEach(function(keyPart) { + var binding = this.parseKeys(keyPart); + var id = KEY_MODS[binding.hashId] + binding.key; + chain += (chain ? " " : "") + id; + this._addCommandToBinding(chain, "chainKeys"); + }, this); + chain += " "; + } + var binding = this.parseKeys(keyPart); + var id = KEY_MODS[binding.hashId] + binding.key; + this._addCommandToBinding(chain + id, command, position); + }, this); + }; + MultiHashHandler.prototype._addCommandToBinding = function(keyId, command, position) { + var ckb = this.commandKeyBinding, + i; + if (!command) { + delete ckb[keyId]; + } else if (!ckb[keyId] || this.$singleCommand) { + ckb[keyId] = command; + } else { + if (!Array.isArray(ckb[keyId])) { + ckb[keyId] = [ckb[keyId]]; + } else if ((i = ckb[keyId].indexOf(command)) != -1) { + ckb[keyId].splice(i, 1); + } + if (typeof position != "number") { + position = getPosition(command); + } + var commands = ckb[keyId]; + for (i = 0; i < commands.length; i++) { + var other = commands[i]; + var otherPos = getPosition(other); + if (otherPos > position) + break; + } + commands.splice(i, 0, command); + } + }; + MultiHashHandler.prototype.addCommands = function(commands) { + commands && Object.keys(commands).forEach(function(name) { + var command = commands[name]; + if (!command) + return; + if (typeof command === "string") + return this.bindKey(command, name); + if (typeof command === "function") + command = { + exec: command + }; + if (typeof command !== "object") + return; + if (!command.name) + command.name = name; + this.addCommand(command); + }, this); + }; + MultiHashHandler.prototype.removeCommands = function(commands) { + Object.keys(commands).forEach(function(name) { + this.removeCommand(commands[name]); + }, this); + }; + MultiHashHandler.prototype.bindKeys = function(keyList) { + Object.keys(keyList).forEach(function(key) { + this.bindKey(key, keyList[key]); + }, this); + }; + MultiHashHandler.prototype._buildKeyHash = function(command) { + this.bindKey(command.bindKey, command); + }; + MultiHashHandler.prototype.parseKeys = function(keys) { + var parts = keys.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(x) { + return x; + }); + var key = parts.pop(); + var keyCode = keyUtil[key]; + if (keyUtil.FUNCTION_KEYS[keyCode]) + key = keyUtil.FUNCTION_KEYS[keyCode].toLowerCase(); + else if (!parts.length) + return { + key: key, + hashId: -1 + }; + else if (parts.length == 1 && parts[0] == "shift") + return { + key: key.toUpperCase(), + hashId: -1 + }; + var hashId = 0; + for (var i = parts.length; i--;) { + var modifier = keyUtil.KEY_MODS[parts[i]]; + if (modifier == null) { + if (typeof console != "undefined") + console.error("invalid modifier " + parts[i] + " in " + keys); + return false; + } + hashId |= modifier; + } + return { + key: key, + hashId: hashId + }; + }; + MultiHashHandler.prototype.findKeyCommand = function(hashId, keyString) { + var key = KEY_MODS[hashId] + keyString; + return this.commandKeyBinding[key]; + }; + MultiHashHandler.prototype.handleKeyboard = function(data, hashId, keyString, keyCode) { + if (keyCode < 0) + return; + var key = KEY_MODS[hashId] + keyString; + var command = this.commandKeyBinding[key]; + if (data.$keyChain) { + data.$keyChain += " " + key; + command = this.commandKeyBinding[data.$keyChain] || command; + } + if (command) { + if (command == "chainKeys" || command[command.length - 1] == "chainKeys") { + data.$keyChain = data.$keyChain || key; + return { + command: "null" + }; + } + } + if (data.$keyChain) { + if ((!hashId || hashId == 4) && keyString.length == 1) + data.$keyChain = data.$keyChain.slice(0, -key.length - 1); // wait for input + else if (hashId == -1 || keyCode > 0) + data.$keyChain = ""; // reset keyChain + } + return { + command: command + }; + }; + MultiHashHandler.prototype.getStatusText = function(editor, data) { + return data.$keyChain || ""; + }; + return MultiHashHandler; + }()); + + function getPosition(command) { + return typeof command == "object" && command.bindKey && + command.bindKey.position || + (command.isDefault ? -100 : 0); + } + var HashHandler = /** @class */ (function(_super) { + __extends(HashHandler, _super); + + function HashHandler(config, platform) { + var _this = _super.call(this, config, platform) || this; + _this.$singleCommand = true; + return _this; + } + return HashHandler; + }(MultiHashHandler)); + HashHandler.call = function(thisArg, config, platform) { + MultiHashHandler.prototype.$init.call(thisArg, config, platform, true); + }; + MultiHashHandler.call = function(thisArg, config, platform) { + MultiHashHandler.prototype.$init.call(thisArg, config, platform, false); + }; + exports.HashHandler = HashHandler; + exports.MultiHashHandler = MultiHashHandler; + +}); + +define("ace/commands/command_manager", ["require", "exports", "module", "ace/lib/oop", "ace/keyboard/hash_handler", "ace/lib/event_emitter"], function(require, exports, module) { + "use strict"; + var __extends = (this && this.__extends) || (function() { + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ + __proto__: [] + } + instanceof Array && function(d, b) { + d.__proto__ = b; + }) || + function(d, b) { + for (var p in b) + if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); + }; + return function(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + var oop = require("../lib/oop"); + var MultiHashHandler = require("../keyboard/hash_handler").MultiHashHandler; + var EventEmitter = require("../lib/event_emitter").EventEmitter; + var CommandManager = /** @class */ (function(_super) { + __extends(CommandManager, _super); + + function CommandManager(platform, commands) { + var _this = _super.call(this, commands, platform) || this; + _this.byName = _this.commands; + _this.setDefaultHandler("exec", function(e) { + if (!e.args) { + return e.command.exec(e.editor, {}, e.event, true); + } + return e.command.exec(e.editor, e.args, e.event, false); + }); + return _this; + } + CommandManager.prototype.exec = function(command, editor, args) { + if (Array.isArray(command)) { + for (var i = command.length; i--;) { + if (this.exec(command[i], editor, args)) + return true; + } + return false; + } + if (typeof command === "string") + command = this.commands[command]; + if (!this.canExecute(command, editor)) { + return false; + } + var e = { + editor: editor, + command: command, + args: args + }; + e.returnValue = this._emit("exec", e); + this._signal("afterExec", e); + return e.returnValue === false ? false : true; + }; + CommandManager.prototype.canExecute = function(command, editor) { + if (typeof command === "string") + command = this.commands[command]; + if (!command) + return false; + if (editor && editor.$readOnly && !command.readOnly) + return false; + if (this.$checkCommandState != false && command.isAvailable && !command.isAvailable(editor)) + return false; + return true; + }; + CommandManager.prototype.toggleRecording = function(editor) { + if (this.$inReplay) + return; + editor && editor._emit("changeStatus"); + if (this.recording) { + this.macro.pop(); + this.off("exec", this.$addCommandToMacro); + if (!this.macro.length) + this.macro = this.oldMacro; + return this.recording = false; + } + if (!this.$addCommandToMacro) { + this.$addCommandToMacro = function(e) { + this.macro.push([e.command, e.args]); + }.bind(this); + } + this.oldMacro = this.macro; + this.macro = []; + this.on("exec", this.$addCommandToMacro); + return this.recording = true; + }; + CommandManager.prototype.replay = function(editor) { + if (this.$inReplay || !this.macro) + return; + if (this.recording) + return this.toggleRecording(editor); + try { + this.$inReplay = true; + this.macro.forEach(function(x) { + if (typeof x == "string") + this.exec(x, editor); + else + this.exec(x[0], editor, x[1]); + }, this); + } finally { + this.$inReplay = false; + } + }; + CommandManager.prototype.trimMacro = function(m) { + return m.map(function(x) { + if (typeof x[0] != "string") + x[0] = x[0].name; + if (!x[1]) + x = x[0]; + return x; + }); + }; + return CommandManager; + }(MultiHashHandler)); + oop.implement(CommandManager.prototype, EventEmitter); + exports.CommandManager = CommandManager; + +}); + +define("ace/commands/default_commands", ["require", "exports", "module", "ace/lib/lang", "ace/config", "ace/range"], function(require, exports, module) { + "use strict"; + var lang = require("../lib/lang"); + var config = require("../config"); + var Range = require("../range").Range; + + function bindKey(win, mac) { + return { + win: win, + mac: mac + }; + } + exports.commands = [{ + name: "showSettingsMenu", + description: "Show settings menu", + bindKey: bindKey("Ctrl-,", "Command-,"), + exec: function(editor) { + config.loadModule("ace/ext/settings_menu", function(module) { + module.init(editor); + editor.showSettingsMenu(); + }); + }, + readOnly: true + }, { + name: "goToNextError", + description: "Go to next error", + bindKey: bindKey("Alt-E", "F4"), + exec: function(editor) { + config.loadModule("ace/ext/error_marker", function(module) { + module.showErrorMarker(editor, 1); + }); + }, + scrollIntoView: "animate", + readOnly: true + }, { + name: "goToPreviousError", + description: "Go to previous error", + bindKey: bindKey("Alt-Shift-E", "Shift-F4"), + exec: function(editor) { + config.loadModule("ace/ext/error_marker", function(module) { + module.showErrorMarker(editor, -1); + }); + }, + scrollIntoView: "animate", + readOnly: true + }, { + name: "selectall", + description: "Select all", + bindKey: bindKey("Ctrl-A", "Command-A"), + exec: function(editor) { + editor.selectAll(); + }, + readOnly: true + }, { + name: "centerselection", + description: "Center selection", + bindKey: bindKey(null, "Ctrl-L"), + exec: function(editor) { + editor.centerSelection(); + }, + readOnly: true + }, { + name: "gotoline", + description: "Go to line...", + bindKey: bindKey("Ctrl-L", "Command-L"), + exec: function(editor, line) { + if (typeof line === "number" && !isNaN(line)) + editor.gotoLine(line); + editor.prompt({ + $type: "gotoLine" + }); + }, + readOnly: true + }, { + name: "fold", + bindKey: bindKey("Alt-L|Ctrl-F1", "Command-Alt-L|Command-F1"), + exec: function(editor) { + editor.session.toggleFold(false); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "unfold", + bindKey: bindKey("Alt-Shift-L|Ctrl-Shift-F1", "Command-Alt-Shift-L|Command-Shift-F1"), + exec: function(editor) { + editor.session.toggleFold(true); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "toggleFoldWidget", + description: "Toggle fold widget", + bindKey: bindKey("F2", "F2"), + exec: function(editor) { + editor.session.toggleFoldWidget(); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "toggleParentFoldWidget", + description: "Toggle parent fold widget", + bindKey: bindKey("Alt-F2", "Alt-F2"), + exec: function(editor) { + editor.session.toggleFoldWidget(true); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "foldall", + description: "Fold all", + bindKey: bindKey(null, "Ctrl-Command-Option-0"), + exec: function(editor) { + editor.session.foldAll(); + }, + scrollIntoView: "center", + readOnly: true + }, { + name: "foldAllComments", + description: "Fold all comments", + bindKey: bindKey(null, "Ctrl-Command-Option-0"), + exec: function(editor) { + editor.session.foldAllComments(); + }, + scrollIntoView: "center", + readOnly: true + }, { + name: "foldOther", + description: "Fold other", + bindKey: bindKey("Alt-0", "Command-Option-0"), + exec: function(editor) { + editor.session.foldAll(); + editor.session.unfold(editor.selection.getAllRanges()); + }, + scrollIntoView: "center", + readOnly: true + }, { + name: "unfoldall", + description: "Unfold all", + bindKey: bindKey("Alt-Shift-0", "Command-Option-Shift-0"), + exec: function(editor) { + editor.session.unfold(); + }, + scrollIntoView: "center", + readOnly: true + }, { + name: "findnext", + description: "Find next", + bindKey: bindKey("Ctrl-K", "Command-G"), + exec: function(editor) { + editor.findNext(); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "findprevious", + description: "Find previous", + bindKey: bindKey("Ctrl-Shift-K", "Command-Shift-G"), + exec: function(editor) { + editor.findPrevious(); + }, + multiSelectAction: "forEach", + scrollIntoView: "center", + readOnly: true + }, { + name: "selectOrFindNext", + description: "Select or find next", + bindKey: bindKey("Alt-K", "Ctrl-G"), + exec: function(editor) { + if (editor.selection.isEmpty()) + editor.selection.selectWord(); + else + editor.findNext(); + }, + readOnly: true + }, { + name: "selectOrFindPrevious", + description: "Select or find previous", + bindKey: bindKey("Alt-Shift-K", "Ctrl-Shift-G"), + exec: function(editor) { + if (editor.selection.isEmpty()) + editor.selection.selectWord(); + else + editor.findPrevious(); + }, + readOnly: true + }, { + name: "find", + description: "Find", + bindKey: bindKey("Ctrl-F", "Command-F"), + exec: function(editor) { + config.loadModule("ace/ext/searchbox", function(e) { + e.Search(editor); + }); + }, + readOnly: true + }, { + name: "overwrite", + description: "Overwrite", + bindKey: "Insert", + exec: function(editor) { + editor.toggleOverwrite(); + }, + readOnly: true + }, { + name: "selecttostart", + description: "Select to start", + bindKey: bindKey("Ctrl-Shift-Home", "Command-Shift-Home|Command-Shift-Up"), + exec: function(editor) { + editor.getSelection().selectFileStart(); + }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, { + name: "gotostart", + description: "Go to start", + bindKey: bindKey("Ctrl-Home", "Command-Home|Command-Up"), + exec: function(editor) { + editor.navigateFileStart(); + }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, { + name: "selectup", + description: "Select up", + bindKey: bindKey("Shift-Up", "Shift-Up|Ctrl-Shift-P"), + exec: function(editor) { + editor.getSelection().selectUp(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "golineup", + description: "Go line up", + bindKey: bindKey("Up", "Up|Ctrl-P"), + exec: function(editor, args) { + editor.navigateUp(args.times); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selecttoend", + description: "Select to end", + bindKey: bindKey("Ctrl-Shift-End", "Command-Shift-End|Command-Shift-Down"), + exec: function(editor) { + editor.getSelection().selectFileEnd(); + }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, { + name: "gotoend", + description: "Go to end", + bindKey: bindKey("Ctrl-End", "Command-End|Command-Down"), + exec: function(editor) { + editor.navigateFileEnd(); + }, + multiSelectAction: "forEach", + readOnly: true, + scrollIntoView: "animate", + aceCommandGroup: "fileJump" + }, { + name: "selectdown", + description: "Select down", + bindKey: bindKey("Shift-Down", "Shift-Down|Ctrl-Shift-N"), + exec: function(editor) { + editor.getSelection().selectDown(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "golinedown", + description: "Go line down", + bindKey: bindKey("Down", "Down|Ctrl-N"), + exec: function(editor, args) { + editor.navigateDown(args.times); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectwordleft", + description: "Select word left", + bindKey: bindKey("Ctrl-Shift-Left", "Option-Shift-Left"), + exec: function(editor) { + editor.getSelection().selectWordLeft(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotowordleft", + description: "Go to word left", + bindKey: bindKey("Ctrl-Left", "Option-Left"), + exec: function(editor) { + editor.navigateWordLeft(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selecttolinestart", + description: "Select to line start", + bindKey: bindKey("Alt-Shift-Left", "Command-Shift-Left|Ctrl-Shift-A"), + exec: function(editor) { + editor.getSelection().selectLineStart(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotolinestart", + description: "Go to line start", + bindKey: bindKey("Alt-Left|Home", "Command-Left|Home|Ctrl-A"), + exec: function(editor) { + editor.navigateLineStart(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectleft", + description: "Select left", + bindKey: bindKey("Shift-Left", "Shift-Left|Ctrl-Shift-B"), + exec: function(editor) { + editor.getSelection().selectLeft(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotoleft", + description: "Go to left", + bindKey: bindKey("Left", "Left|Ctrl-B"), + exec: function(editor, args) { + editor.navigateLeft(args.times); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectwordright", + description: "Select word right", + bindKey: bindKey("Ctrl-Shift-Right", "Option-Shift-Right"), + exec: function(editor) { + editor.getSelection().selectWordRight(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotowordright", + description: "Go to word right", + bindKey: bindKey("Ctrl-Right", "Option-Right"), + exec: function(editor) { + editor.navigateWordRight(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selecttolineend", + description: "Select to line end", + bindKey: bindKey("Alt-Shift-Right", "Command-Shift-Right|Shift-End|Ctrl-Shift-E"), + exec: function(editor) { + editor.getSelection().selectLineEnd(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotolineend", + description: "Go to line end", + bindKey: bindKey("Alt-Right|End", "Command-Right|End|Ctrl-E"), + exec: function(editor) { + editor.navigateLineEnd(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectright", + description: "Select right", + bindKey: bindKey("Shift-Right", "Shift-Right"), + exec: function(editor) { + editor.getSelection().selectRight(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "gotoright", + description: "Go to right", + bindKey: bindKey("Right", "Right|Ctrl-F"), + exec: function(editor, args) { + editor.navigateRight(args.times); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectpagedown", + description: "Select page down", + bindKey: "Shift-PageDown", + exec: function(editor) { + editor.selectPageDown(); + }, + readOnly: true + }, { + name: "pagedown", + description: "Page down", + bindKey: bindKey(null, "Option-PageDown"), + exec: function(editor) { + editor.scrollPageDown(); + }, + readOnly: true + }, { + name: "gotopagedown", + description: "Go to page down", + bindKey: bindKey("PageDown", "PageDown|Ctrl-V"), + exec: function(editor) { + editor.gotoPageDown(); + }, + readOnly: true + }, { + name: "selectpageup", + description: "Select page up", + bindKey: "Shift-PageUp", + exec: function(editor) { + editor.selectPageUp(); + }, + readOnly: true + }, { + name: "pageup", + description: "Page up", + bindKey: bindKey(null, "Option-PageUp"), + exec: function(editor) { + editor.scrollPageUp(); + }, + readOnly: true + }, { + name: "gotopageup", + description: "Go to page up", + bindKey: "PageUp", + exec: function(editor) { + editor.gotoPageUp(); + }, + readOnly: true + }, { + name: "scrollup", + description: "Scroll up", + bindKey: bindKey("Ctrl-Up", null), + exec: function(e) { + e.renderer.scrollBy(0, -2 * e.renderer.layerConfig.lineHeight); + }, + readOnly: true + }, { + name: "scrolldown", + description: "Scroll down", + bindKey: bindKey("Ctrl-Down", null), + exec: function(e) { + e.renderer.scrollBy(0, 2 * e.renderer.layerConfig.lineHeight); + }, + readOnly: true + }, { + name: "selectlinestart", + description: "Select line start", + bindKey: "Shift-Home", + exec: function(editor) { + editor.getSelection().selectLineStart(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectlineend", + description: "Select line end", + bindKey: "Shift-End", + exec: function(editor) { + editor.getSelection().selectLineEnd(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "togglerecording", + description: "Toggle recording", + bindKey: bindKey("Ctrl-Alt-E", "Command-Option-E"), + exec: function(editor) { + editor.commands.toggleRecording(editor); + }, + readOnly: true + }, { + name: "replaymacro", + description: "Replay macro", + bindKey: bindKey("Ctrl-Shift-E", "Command-Shift-E"), + exec: function(editor) { + editor.commands.replay(editor); + }, + readOnly: true + }, { + name: "jumptomatching", + description: "Jump to matching", + bindKey: bindKey("Ctrl-\\|Ctrl-P", "Command-\\"), + exec: function(editor) { + editor.jumpToMatching(); + }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true + }, { + name: "selecttomatching", + description: "Select to matching", + bindKey: bindKey("Ctrl-Shift-\\|Ctrl-Shift-P", "Command-Shift-\\"), + exec: function(editor) { + editor.jumpToMatching(true); + }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true + }, { + name: "expandToMatching", + description: "Expand to matching", + bindKey: bindKey("Ctrl-Shift-M", "Ctrl-Shift-M"), + exec: function(editor) { + editor.jumpToMatching(true, true); + }, + multiSelectAction: "forEach", + scrollIntoView: "animate", + readOnly: true + }, { + name: "passKeysToBrowser", + description: "Pass keys to browser", + bindKey: bindKey(null, null), + exec: function() {}, + passEvent: true, + readOnly: true + }, { + name: "copy", + description: "Copy", + exec: function(editor) {}, + readOnly: true + }, + { + name: "cut", + description: "Cut", + exec: function(editor) { + var cutLine = editor.$copyWithEmptySelection && editor.selection.isEmpty(); + var range = cutLine ? editor.selection.getLineRange() : editor.selection.getRange(); + editor._emit("cut", range); + if (!range.isEmpty()) + editor.session.remove(range); + editor.clearSelection(); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, { + name: "paste", + description: "Paste", + exec: function(editor, args) { + editor.$handlePaste(args); + }, + scrollIntoView: "cursor" + }, { + name: "removeline", + description: "Remove line", + bindKey: bindKey("Ctrl-D", "Command-D"), + exec: function(editor) { + editor.removeLines(); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEachLine" + }, { + name: "duplicateSelection", + description: "Duplicate selection", + bindKey: bindKey("Ctrl-Shift-D", "Command-Shift-D"), + exec: function(editor) { + editor.duplicateSelection(); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, { + name: "sortlines", + description: "Sort lines", + bindKey: bindKey("Ctrl-Alt-S", "Command-Alt-S"), + exec: function(editor) { + editor.sortLines(); + }, + scrollIntoView: "selection", + multiSelectAction: "forEachLine" + }, { + name: "togglecomment", + description: "Toggle comment", + bindKey: bindKey("Ctrl-/", "Command-/"), + exec: function(editor) { + editor.toggleCommentLines(); + }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" + }, { + name: "toggleBlockComment", + description: "Toggle block comment", + bindKey: bindKey("Ctrl-Shift-/", "Command-Shift-/"), + exec: function(editor) { + editor.toggleBlockComment(); + }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" + }, { + name: "modifyNumberUp", + description: "Modify number up", + bindKey: bindKey("Ctrl-Shift-Up", "Alt-Shift-Up"), + exec: function(editor) { + editor.modifyNumber(1); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, { + name: "modifyNumberDown", + description: "Modify number down", + bindKey: bindKey("Ctrl-Shift-Down", "Alt-Shift-Down"), + exec: function(editor) { + editor.modifyNumber(-1); + }, + scrollIntoView: "cursor", + multiSelectAction: "forEach" + }, { + name: "replace", + description: "Replace", + bindKey: bindKey("Ctrl-H", "Command-Option-F"), + exec: function(editor) { + config.loadModule("ace/ext/searchbox", function(e) { + e.Search(editor, true); + }); + } + }, { + name: "undo", + description: "Undo", + bindKey: bindKey("Ctrl-Z", "Command-Z"), + exec: function(editor) { + editor.undo(); + } + }, { + name: "redo", + description: "Redo", + bindKey: bindKey("Ctrl-Shift-Z|Ctrl-Y", "Command-Shift-Z|Command-Y"), + exec: function(editor) { + editor.redo(); + } + }, { + name: "copylinesup", + description: "Copy lines up", + bindKey: bindKey("Alt-Shift-Up", "Command-Option-Up"), + exec: function(editor) { + editor.copyLinesUp(); + }, + scrollIntoView: "cursor" + }, { + name: "movelinesup", + description: "Move lines up", + bindKey: bindKey("Alt-Up", "Option-Up"), + exec: function(editor) { + editor.moveLinesUp(); + }, + scrollIntoView: "cursor" + }, { + name: "copylinesdown", + description: "Copy lines down", + bindKey: bindKey("Alt-Shift-Down", "Command-Option-Down"), + exec: function(editor) { + editor.copyLinesDown(); + }, + scrollIntoView: "cursor" + }, { + name: "movelinesdown", + description: "Move lines down", + bindKey: bindKey("Alt-Down", "Option-Down"), + exec: function(editor) { + editor.moveLinesDown(); + }, + scrollIntoView: "cursor" + }, { + name: "del", + description: "Delete", + bindKey: bindKey("Delete", "Delete|Ctrl-D|Shift-Delete"), + exec: function(editor) { + editor.remove("right"); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "backspace", + description: "Backspace", + bindKey: bindKey("Shift-Backspace|Backspace", "Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"), + exec: function(editor) { + editor.remove("left"); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "cut_or_delete", + description: "Cut or delete", + bindKey: bindKey("Shift-Delete", null), + exec: function(editor) { + if (editor.selection.isEmpty()) { + editor.remove("left"); + } else { + return false; + } + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removetolinestart", + description: "Remove to line start", + bindKey: bindKey("Alt-Backspace", "Command-Backspace"), + exec: function(editor) { + editor.removeToLineStart(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removetolineend", + description: "Remove to line end", + bindKey: bindKey("Alt-Delete", "Ctrl-K|Command-Delete"), + exec: function(editor) { + editor.removeToLineEnd(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removetolinestarthard", + description: "Remove to line start hard", + bindKey: bindKey("Ctrl-Shift-Backspace", null), + exec: function(editor) { + var range = editor.selection.getRange(); + range.start.column = 0; + editor.session.remove(range); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removetolineendhard", + description: "Remove to line end hard", + bindKey: bindKey("Ctrl-Shift-Delete", null), + exec: function(editor) { + var range = editor.selection.getRange(); + range.end.column = Number.MAX_VALUE; + editor.session.remove(range); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removewordleft", + description: "Remove word left", + bindKey: bindKey("Ctrl-Backspace", "Alt-Backspace|Ctrl-Alt-Backspace"), + exec: function(editor) { + editor.removeWordLeft(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "removewordright", + description: "Remove word right", + bindKey: bindKey("Ctrl-Delete", "Alt-Delete"), + exec: function(editor) { + editor.removeWordRight(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "outdent", + description: "Outdent", + bindKey: bindKey("Shift-Tab", "Shift-Tab"), + exec: function(editor) { + editor.blockOutdent(); + }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" + }, { + name: "indent", + description: "Indent", + bindKey: bindKey("Tab", "Tab"), + exec: function(editor) { + editor.indent(); + }, + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" + }, { + name: "blockoutdent", + description: "Block outdent", + bindKey: bindKey("Ctrl-[", "Ctrl-["), + exec: function(editor) { + editor.blockOutdent(); + }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" + }, { + name: "blockindent", + description: "Block indent", + bindKey: bindKey("Ctrl-]", "Ctrl-]"), + exec: function(editor) { + editor.blockIndent(); + }, + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" + }, { + name: "insertstring", + description: "Insert string", + exec: function(editor, str) { + editor.insert(str); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "inserttext", + description: "Insert text", + exec: function(editor, args) { + editor.insert(lang.stringRepeat(args.text || "", args.times || 1)); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "splitline", + description: "Split line", + bindKey: bindKey(null, "Ctrl-O"), + exec: function(editor) { + editor.splitLine(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "transposeletters", + description: "Transpose letters", + bindKey: bindKey("Alt-Shift-X", "Ctrl-T"), + exec: function(editor) { + editor.transposeLetters(); + }, + multiSelectAction: function(editor) { + editor.transposeSelections(1); + }, + scrollIntoView: "cursor" + }, { + name: "touppercase", + description: "To uppercase", + bindKey: bindKey("Ctrl-U", "Ctrl-U"), + exec: function(editor) { + editor.toUpperCase(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "tolowercase", + description: "To lowercase", + bindKey: bindKey("Ctrl-Shift-U", "Ctrl-Shift-U"), + exec: function(editor) { + editor.toLowerCase(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "autoindent", + description: "Auto Indent", + bindKey: bindKey(null, null), + exec: function(editor) { + editor.autoIndent(); + }, + scrollIntoView: "animate" + }, { + name: "expandtoline", + description: "Expand to line", + bindKey: bindKey("Ctrl-Shift-L", "Command-Shift-L"), + exec: function(editor) { + var range = editor.selection.getRange(); + range.start.column = range.end.column = 0; + range.end.row++; + editor.selection.setRange(range, false); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor", + readOnly: true + }, { + name: "openlink", + bindKey: bindKey("Ctrl+F3", "F3"), + exec: function(editor) { + editor.openLink(); + } + }, { + name: "joinlines", + description: "Join lines", + bindKey: bindKey(null, null), + exec: function(editor) { + var isBackwards = editor.selection.isBackwards(); + var selectionStart = isBackwards ? editor.selection.getSelectionLead() : editor.selection.getSelectionAnchor(); + var selectionEnd = isBackwards ? editor.selection.getSelectionAnchor() : editor.selection.getSelectionLead(); + var firstLineEndCol = editor.session.doc.getLine(selectionStart.row).length; + var selectedText = editor.session.doc.getTextRange(editor.selection.getRange()); + var selectedCount = selectedText.replace(/\n\s*/, " ").length; + var insertLine = editor.session.doc.getLine(selectionStart.row); + for (var i = selectionStart.row + 1; i <= selectionEnd.row + 1; i++) { + var curLine = lang.stringTrimLeft(lang.stringTrimRight(editor.session.doc.getLine(i))); + if (curLine.length !== 0) { + curLine = " " + curLine; + } + insertLine += curLine; + } + if (selectionEnd.row + 1 < (editor.session.doc.getLength() - 1)) { + insertLine += editor.session.doc.getNewLineCharacter(); + } + editor.clearSelection(); + editor.session.doc.replace(new Range(selectionStart.row, 0, selectionEnd.row + 2, 0), insertLine); + if (selectedCount > 0) { + editor.selection.moveCursorTo(selectionStart.row, selectionStart.column); + editor.selection.selectTo(selectionStart.row, selectionStart.column + selectedCount); + } else { + firstLineEndCol = editor.session.doc.getLine(selectionStart.row).length > firstLineEndCol ? (firstLineEndCol + 1) : firstLineEndCol; + editor.selection.moveCursorTo(selectionStart.row, firstLineEndCol); + } + }, + multiSelectAction: "forEach", + readOnly: true + }, { + name: "invertSelection", + description: "Invert selection", + bindKey: bindKey(null, null), + exec: function(editor) { + var endRow = editor.session.doc.getLength() - 1; + var endCol = editor.session.doc.getLine(endRow).length; + var ranges = editor.selection.rangeList.ranges; + var newRanges = []; + if (ranges.length < 1) { + ranges = [editor.selection.getRange()]; + } + for (var i = 0; i < ranges.length; i++) { + if (i == (ranges.length - 1)) { + if (!(ranges[i].end.row === endRow && ranges[i].end.column === endCol)) { + newRanges.push(new Range(ranges[i].end.row, ranges[i].end.column, endRow, endCol)); + } + } + if (i === 0) { + if (!(ranges[i].start.row === 0 && ranges[i].start.column === 0)) { + newRanges.push(new Range(0, 0, ranges[i].start.row, ranges[i].start.column)); + } + } else { + newRanges.push(new Range(ranges[i - 1].end.row, ranges[i - 1].end.column, ranges[i].start.row, ranges[i].start.column)); + } + } + editor.exitMultiSelectMode(); + editor.clearSelection(); + for (var i = 0; i < newRanges.length; i++) { + editor.selection.addRange(newRanges[i], false); + } + }, + readOnly: true, + scrollIntoView: "none" + }, { + name: "addLineAfter", + description: "Add new line after the current line", + exec: function(editor) { + editor.selection.clearSelection(); + editor.navigateLineEnd(); + editor.insert("\n"); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "addLineBefore", + description: "Add new line before the current line", + exec: function(editor) { + editor.selection.clearSelection(); + var cursor = editor.getCursorPosition(); + editor.selection.moveTo(cursor.row - 1, Number.MAX_VALUE); + editor.insert("\n"); + if (cursor.row === 0) + editor.navigateUp(); + }, + multiSelectAction: "forEach", + scrollIntoView: "cursor" + }, { + name: "openCommandPallete", + exec: function(editor) { + console.warn("This is an obsolete command. Please use `openCommandPalette` instead."); + editor.prompt({ + $type: "commands" + }); + }, + readOnly: true + }, { + name: "openCommandPalette", + description: "Open command palette", + bindKey: bindKey("F1", "F1"), + exec: function(editor) { + editor.prompt({ + $type: "commands" + }); + }, + readOnly: true + }, { + name: "modeSelect", + description: "Change language mode...", + bindKey: bindKey(null, null), + exec: function(editor) { + editor.prompt({ + $type: "modes" + }); + }, + readOnly: true + } + ]; + for (var i = 1; i < 9; i++) { + exports.commands.push({ + name: "foldToLevel" + i, + description: "Fold To Level " + i, + level: i, + exec: function(editor) { + editor.session.foldToLevel(this.level); + }, + scrollIntoView: "center", + readOnly: true + }); + } + +}); + +define("ace/line_widgets", ["require", "exports", "module", "ace/lib/dom"], function(require, exports, module) { + "use strict"; + var dom = require("./lib/dom"); + var LineWidgets = /** @class */ (function() { + function LineWidgets(session) { + this.session = session; + this.session.widgetManager = this; + this.session.getRowLength = this.getRowLength; + this.session.$getWidgetScreenLength = this.$getWidgetScreenLength; + this.updateOnChange = this.updateOnChange.bind(this); + this.renderWidgets = this.renderWidgets.bind(this); + this.measureWidgets = this.measureWidgets.bind(this); + this.session._changedWidgets = []; + this.$onChangeEditor = this.$onChangeEditor.bind(this); + this.session.on("change", this.updateOnChange); + this.session.on("changeFold", this.updateOnFold); + this.session.on("changeEditor", this.$onChangeEditor); + } + LineWidgets.prototype.getRowLength = function(row) { + var h; + if (this.lineWidgets) + h = this.lineWidgets[row] && this.lineWidgets[row].rowCount || 0; + else + h = 0; + if (!this["$useWrapMode"] || !this["$wrapData"][row]) { + return 1 + h; + } else { + return this["$wrapData"][row].length + 1 + h; + } + }; + LineWidgets.prototype.$getWidgetScreenLength = function() { + var screenRows = 0; + this.lineWidgets.forEach(function(w) { + if (w && w.rowCount && !w.hidden) + screenRows += w.rowCount; + }); + return screenRows; + }; + LineWidgets.prototype.$onChangeEditor = function(e) { + this.attach(e.editor); + }; + LineWidgets.prototype.attach = function(editor) { + if (editor && editor.widgetManager && editor.widgetManager != this) + editor.widgetManager.detach(); + if (this.editor == editor) + return; + this.detach(); + this.editor = editor; + if (editor) { + editor.widgetManager = this; + editor.renderer.on("beforeRender", this.measureWidgets); + editor.renderer.on("afterRender", this.renderWidgets); + } + }; + LineWidgets.prototype.detach = function(e) { + var editor = this.editor; + if (!editor) + return; + this.editor = null; + editor.widgetManager = null; + editor.renderer.off("beforeRender", this.measureWidgets); + editor.renderer.off("afterRender", this.renderWidgets); + var lineWidgets = this.session.lineWidgets; + lineWidgets && lineWidgets.forEach(function(w) { + if (w && w.el && w.el.parentNode) { + w._inDocument = false; + w.el.parentNode.removeChild(w.el); + } + }); + }; + LineWidgets.prototype.updateOnFold = function(e, session) { + var lineWidgets = session.lineWidgets; + if (!lineWidgets || !e.action) + return; + var fold = e.data; + var start = fold.start.row; + var end = fold.end.row; + var hide = e.action == "add"; + for (var i = start + 1; i < end; i++) { + if (lineWidgets[i]) + lineWidgets[i].hidden = hide; + } + if (lineWidgets[end]) { + if (hide) { + if (!lineWidgets[start]) + lineWidgets[start] = lineWidgets[end]; + else + lineWidgets[end].hidden = hide; + } else { + if (lineWidgets[start] == lineWidgets[end]) + lineWidgets[start] = undefined; + lineWidgets[end].hidden = hide; + } + } + }; + LineWidgets.prototype.updateOnChange = function(delta) { + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) + return; + var startRow = delta.start.row; + var len = delta.end.row - startRow; + if (len === 0) {} else if (delta.action == "remove") { + var removed = lineWidgets.splice(startRow + 1, len); + if (!lineWidgets[startRow] && removed[removed.length - 1]) { + lineWidgets[startRow] = removed.pop(); + } + removed.forEach(function(w) { + w && this.removeLineWidget(w); + }, this); + this.$updateRows(); + } else { + var args = new Array(len); + if (lineWidgets[startRow] && lineWidgets[startRow].column != null) { + if (delta.start.column > lineWidgets[startRow].column) + startRow++; + } + args.unshift(startRow, 0); + lineWidgets.splice.apply(lineWidgets, args); + this.$updateRows(); + } + }; + LineWidgets.prototype.$updateRows = function() { + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) + return; + var noWidgets = true; + lineWidgets.forEach(function(w, i) { + if (w) { + noWidgets = false; + w.row = i; + while (w.$oldWidget) { + w.$oldWidget.row = i; + w = w.$oldWidget; + } + } + }); + if (noWidgets) + this.session.lineWidgets = null; + }; + LineWidgets.prototype.$registerLineWidget = function(w) { + if (!this.session.lineWidgets) + this.session.lineWidgets = new Array(this.session.getLength()); + var old = this.session.lineWidgets[w.row]; + if (old) { + w.$oldWidget = old; + if (old.el && old.el.parentNode) { + old.el.parentNode.removeChild(old.el); + old._inDocument = false; + } + } + this.session.lineWidgets[w.row] = w; + return w; + }; + LineWidgets.prototype.addLineWidget = function(w) { + this.$registerLineWidget(w); + w.session = this.session; + if (!this.editor) + return w; + var renderer = this.editor.renderer; + if (w.html && !w.el) { + w.el = dom.createElement("div"); + w.el.innerHTML = w.html; + } + if (w.text && !w.el) { + w.el = dom.createElement("div"); + w.el.textContent = w.text; + } + if (w.el) { + dom.addCssClass(w.el, "ace_lineWidgetContainer"); + if (w.className) { + dom.addCssClass(w.el, w.className); + } + w.el.style.position = "absolute"; + w.el.style.zIndex = "5"; + renderer.container.appendChild(w.el); + w._inDocument = true; + if (!w.coverGutter) { + w.el.style.zIndex = "3"; + } + if (w.pixelHeight == null) { + w.pixelHeight = w.el.offsetHeight; + } + } + if (w.rowCount == null) { + w.rowCount = w.pixelHeight / renderer.layerConfig.lineHeight; + } + var fold = this.session.getFoldAt(w.row, 0); + w.$fold = fold; + if (fold) { + var lineWidgets = this.session.lineWidgets; + if (w.row == fold.end.row && !lineWidgets[fold.start.row]) + lineWidgets[fold.start.row] = w; + else + w.hidden = true; + } + this.session._emit("changeFold", { + data: { + start: { + row: w.row + } + } + }); + this.$updateRows(); + this.renderWidgets(null, renderer); + this.onWidgetChanged(w); + return w; + }; + LineWidgets.prototype.removeLineWidget = function(w) { + w._inDocument = false; + w.session = null; + if (w.el && w.el.parentNode) + w.el.parentNode.removeChild(w.el); + if (w.editor && w.editor.destroy) + try { + w.editor.destroy(); + } + catch (e) {} + if (this.session.lineWidgets) { + var w1 = this.session.lineWidgets[w.row]; + if (w1 == w) { + this.session.lineWidgets[w.row] = w.$oldWidget; + if (w.$oldWidget) + this.onWidgetChanged(w.$oldWidget); + } else { + while (w1) { + if (w1.$oldWidget == w) { + w1.$oldWidget = w.$oldWidget; + break; + } + w1 = w1.$oldWidget; + } + } + } + this.session._emit("changeFold", { + data: { + start: { + row: w.row + } + } + }); + this.$updateRows(); + }; + LineWidgets.prototype.getWidgetsAtRow = function(row) { + var lineWidgets = this.session.lineWidgets; + var w = lineWidgets && lineWidgets[row]; + var list = []; + while (w) { + list.push(w); + w = w.$oldWidget; + } + return list; + }; + LineWidgets.prototype.onWidgetChanged = function(w) { + this.session._changedWidgets.push(w); + this.editor && this.editor.renderer.updateFull(); + }; + LineWidgets.prototype.measureWidgets = function(e, renderer) { + var changedWidgets = this.session._changedWidgets; + var config = renderer.layerConfig; + if (!changedWidgets || !changedWidgets.length) + return; + var min = Infinity; + for (var i = 0; i < changedWidgets.length; i++) { + var w = changedWidgets[i]; + if (!w || !w.el) + continue; + if (w.session != this.session) + continue; + if (!w._inDocument) { + if (this.session.lineWidgets[w.row] != w) + continue; + w._inDocument = true; + renderer.container.appendChild(w.el); + } + w.h = w.el.offsetHeight; + if (!w.fixedWidth) { + w.w = w.el.offsetWidth; + w.screenWidth = Math.ceil(w.w / config.characterWidth); + } + var rowCount = w.h / config.lineHeight; + if (w.coverLine) { + rowCount -= this.session.getRowLineCount(w.row); + if (rowCount < 0) + rowCount = 0; + } + if (w.rowCount != rowCount) { + w.rowCount = rowCount; + if (w.row < min) + min = w.row; + } + } + if (min != Infinity) { + this.session._emit("changeFold", { + data: { + start: { + row: min + } + } + }); + this.session.lineWidgetWidth = null; + } + this.session._changedWidgets = []; + }; + LineWidgets.prototype.renderWidgets = function(e, renderer) { + var config = renderer.layerConfig; + var lineWidgets = this.session.lineWidgets; + if (!lineWidgets) + return; + var first = Math.min(this.firstRow, config.firstRow); + var last = Math.max(this.lastRow, config.lastRow, lineWidgets.length); + while (first > 0 && !lineWidgets[first]) + first--; + this.firstRow = config.firstRow; + this.lastRow = config.lastRow; + renderer.$cursorLayer.config = config; + for (var i = first; i <= last; i++) { + var w = lineWidgets[i]; + if (!w || !w.el) + continue; + if (w.hidden) { + w.el.style.top = -100 - (w.pixelHeight || 0) + "px"; + continue; + } + if (!w._inDocument) { + w._inDocument = true; + renderer.container.appendChild(w.el); + } + var top = renderer.$cursorLayer.getPixelPosition({ + row: i, + column: 0 + }, true).top; + if (!w.coverLine) + top += config.lineHeight * this.session.getRowLineCount(w.row); + w.el.style.top = top - config.offset + "px"; + var left = w.coverGutter ? 0 : renderer.gutterWidth; + if (!w.fixedWidth) + left -= renderer.scrollLeft; + w.el.style.left = left + "px"; + if (w.fullWidth && w.screenWidth) { + w.el.style.minWidth = config.width + 2 * config.padding + "px"; + } + if (w.fixedWidth) { + w.el.style.right = renderer.scrollBar.getWidth() + "px"; + } else { + w.el.style.right = ""; + } + } + }; + return LineWidgets; + }()); + exports.LineWidgets = LineWidgets; + +}); + +define("ace/keyboard/gutter_handler", ["require", "exports", "module", "ace/lib/keys", "ace/mouse/default_gutter_handler"], function(require, exports, module) { + "use strict"; + var keys = require('../lib/keys'); + var GutterTooltip = require("../mouse/default_gutter_handler").GutterTooltip; + var GutterKeyboardHandler = /** @class */ (function() { + function GutterKeyboardHandler(editor) { + this.editor = editor; + this.gutterLayer = editor.renderer.$gutterLayer; + this.element = editor.renderer.$gutter; + this.lines = editor.renderer.$gutterLayer.$lines; + this.activeRowIndex = null; + this.activeLane = null; + this.annotationTooltip = new GutterTooltip(this.editor); + } + GutterKeyboardHandler.prototype.addListener = function() { + this.element.addEventListener("keydown", this.$onGutterKeyDown.bind(this)); + this.element.addEventListener("focusout", this.$blurGutter.bind(this)); + this.editor.on("mousewheel", this.$blurGutter.bind(this)); + }; + GutterKeyboardHandler.prototype.removeListener = function() { + this.element.removeEventListener("keydown", this.$onGutterKeyDown.bind(this)); + this.element.removeEventListener("focusout", this.$blurGutter.bind(this)); + this.editor.off("mousewheel", this.$blurGutter.bind(this)); + }; + GutterKeyboardHandler.prototype.$onGutterKeyDown = function(e) { + if (this.annotationTooltip.isOpen) { + e.preventDefault(); + if (e.keyCode === keys["escape"]) + this.annotationTooltip.hideTooltip(); + return; + } + if (e.target === this.element) { + if (e.keyCode != keys["enter"]) { + return; + } + e.preventDefault(); + var row = this.editor.getCursorPosition().row; + if (!this.editor.isRowVisible(row)) + this.editor.scrollToLine(row, true, true); + setTimeout( + function() { + var index = this.$rowToRowIndex(this.gutterLayer.$cursorCell.row); + var nearestFoldIndex = this.$findNearestFoldWidget(index); + var nearestAnnotationIndex = this.$findNearestAnnotation(index); + if (nearestFoldIndex === null && nearestAnnotationIndex === null) + return; + if (nearestFoldIndex === null && nearestAnnotationIndex !== null) { + this.activeRowIndex = nearestAnnotationIndex; + this.activeLane = "annotation"; + this.$focusAnnotation(this.activeRowIndex); + return; + } + if (nearestFoldIndex !== null && nearestAnnotationIndex === null) { + this.activeRowIndex = nearestFoldIndex; + this.activeLane = "fold"; + this.$focusFoldWidget(this.activeRowIndex); + return; + } + if (Math.abs(nearestAnnotationIndex - index) < Math.abs(nearestFoldIndex - index)) { + this.activeRowIndex = nearestAnnotationIndex; + this.activeLane = "annotation"; + this.$focusAnnotation(this.activeRowIndex); + return; + } else { + this.activeRowIndex = nearestFoldIndex; + this.activeLane = "fold"; + this.$focusFoldWidget(this.activeRowIndex); + return; + } + }.bind(this), 10); + return; + } + this.$handleGutterKeyboardInteraction(e); + setTimeout(function() { + this.editor._signal("gutterkeydown", new GutterKeyboardEvent(e, this)); + }.bind(this), 10); + }; + GutterKeyboardHandler.prototype.$handleGutterKeyboardInteraction = function(e) { + if (e.keyCode === keys["tab"]) { + e.preventDefault(); + return; + } + if (e.keyCode === keys["escape"]) { + e.preventDefault(); + this.$blurGutter(); + this.element.focus(); + this.lane = null; + return; + } + if (e.keyCode === keys["up"]) { + e.preventDefault(); + switch (this.activeLane) { + case "fold": + this.$moveFoldWidgetUp(); + break; + case "annotation": + this.$moveAnnotationUp(); + break; + } + return; + } + if (e.keyCode === keys["down"]) { + e.preventDefault(); + switch (this.activeLane) { + case "fold": + this.$moveFoldWidgetDown(); + break; + case "annotation": + this.$moveAnnotationDown(); + break; + } + return; + } + if (e.keyCode === keys["left"]) { + e.preventDefault(); + this.$switchLane("annotation"); + return; + } + if (e.keyCode === keys["right"]) { + e.preventDefault(); + this.$switchLane("fold"); + return; + } + if (e.keyCode === keys["enter"] || e.keyCode === keys["space"]) { + e.preventDefault(); + switch (this.activeLane) { + case "fold": + if (this.gutterLayer.session.foldWidgets[this.$rowIndexToRow(this.activeRowIndex)] === 'start') { + var rowFoldingWidget = this.$rowIndexToRow(this.activeRowIndex); + this.editor.session.onFoldWidgetClick(this.$rowIndexToRow(this.activeRowIndex), e); + setTimeout( + function() { + if (this.$rowIndexToRow(this.activeRowIndex) !== rowFoldingWidget) { + this.$blurFoldWidget(this.activeRowIndex); + this.activeRowIndex = this.$rowToRowIndex(rowFoldingWidget); + this.$focusFoldWidget(this.activeRowIndex); + } + }.bind(this), 10); + break; + } else if (this.gutterLayer.session.foldWidgets[this.$rowIndexToRow(this.activeRowIndex)] === 'end') { + break; + } + return; + case "annotation": + var gutterElement = this.lines.cells[this.activeRowIndex].element.childNodes[2]; + var rect = gutterElement.getBoundingClientRect(); + var style = this.annotationTooltip.getElement().style; + style.left = rect.right + "px"; + style.top = rect.bottom + "px"; + this.annotationTooltip.showTooltip(this.$rowIndexToRow(this.activeRowIndex)); + break; + } + return; + } + }; + GutterKeyboardHandler.prototype.$blurGutter = function() { + if (this.activeRowIndex !== null) { + switch (this.activeLane) { + case "fold": + this.$blurFoldWidget(this.activeRowIndex); + break; + case "annotation": + this.$blurAnnotation(this.activeRowIndex); + break; + } + } + if (this.annotationTooltip.isOpen) + this.annotationTooltip.hideTooltip(); + return; + }; + GutterKeyboardHandler.prototype.$isFoldWidgetVisible = function(index) { + var isRowFullyVisible = this.editor.isRowFullyVisible(this.$rowIndexToRow(index)); + var isIconVisible = this.$getFoldWidget(index).style.display !== "none"; + return isRowFullyVisible && isIconVisible; + }; + GutterKeyboardHandler.prototype.$isAnnotationVisible = function(index) { + var isRowFullyVisible = this.editor.isRowFullyVisible(this.$rowIndexToRow(index)); + var isIconVisible = this.$getAnnotation(index).style.display !== "none"; + return isRowFullyVisible && isIconVisible; + }; + GutterKeyboardHandler.prototype.$getFoldWidget = function(index) { + var cell = this.lines.get(index); + var element = cell.element; + return element.childNodes[1]; + }; + GutterKeyboardHandler.prototype.$getAnnotation = function(index) { + var cell = this.lines.get(index); + var element = cell.element; + return element.childNodes[2]; + }; + GutterKeyboardHandler.prototype.$findNearestFoldWidget = function(index) { + if (this.$isFoldWidgetVisible(index)) + return index; + var i = 0; + while (index - i > 0 || index + i < this.lines.getLength() - 1) { + i++; + if (index - i >= 0 && this.$isFoldWidgetVisible(index - i)) + return index - i; + if (index + i <= this.lines.getLength() - 1 && this.$isFoldWidgetVisible(index + i)) + return index + i; + } + return null; + }; + GutterKeyboardHandler.prototype.$findNearestAnnotation = function(index) { + if (this.$isAnnotationVisible(index)) + return index; + var i = 0; + while (index - i > 0 || index + i < this.lines.getLength() - 1) { + i++; + if (index - i >= 0 && this.$isAnnotationVisible(index - i)) + return index - i; + if (index + i <= this.lines.getLength() - 1 && this.$isAnnotationVisible(index + i)) + return index + i; + } + return null; + }; + GutterKeyboardHandler.prototype.$focusFoldWidget = function(index) { + if (index == null) + return; + var foldWidget = this.$getFoldWidget(index); + foldWidget.classList.add(this.editor.renderer.keyboardFocusClassName); + foldWidget.focus(); + }; + GutterKeyboardHandler.prototype.$focusAnnotation = function(index) { + if (index == null) + return; + var annotation = this.$getAnnotation(index); + annotation.classList.add(this.editor.renderer.keyboardFocusClassName); + annotation.focus(); + }; + GutterKeyboardHandler.prototype.$blurFoldWidget = function(index) { + var foldWidget = this.$getFoldWidget(index); + foldWidget.classList.remove(this.editor.renderer.keyboardFocusClassName); + foldWidget.blur(); + }; + GutterKeyboardHandler.prototype.$blurAnnotation = function(index) { + var annotation = this.$getAnnotation(index); + annotation.classList.remove(this.editor.renderer.keyboardFocusClassName); + annotation.blur(); + }; + GutterKeyboardHandler.prototype.$moveFoldWidgetUp = function() { + var index = this.activeRowIndex; + while (index > 0) { + index--; + if (this.$isFoldWidgetVisible(index)) { + this.$blurFoldWidget(this.activeRowIndex); + this.activeRowIndex = index; + this.$focusFoldWidget(this.activeRowIndex); + return; + } + } + return; + }; + GutterKeyboardHandler.prototype.$moveFoldWidgetDown = function() { + var index = this.activeRowIndex; + while (index < this.lines.getLength() - 1) { + index++; + if (this.$isFoldWidgetVisible(index)) { + this.$blurFoldWidget(this.activeRowIndex); + this.activeRowIndex = index; + this.$focusFoldWidget(this.activeRowIndex); + return; + } + } + return; + }; + GutterKeyboardHandler.prototype.$moveAnnotationUp = function() { + var index = this.activeRowIndex; + while (index > 0) { + index--; + if (this.$isAnnotationVisible(index)) { + this.$blurAnnotation(this.activeRowIndex); + this.activeRowIndex = index; + this.$focusAnnotation(this.activeRowIndex); + return; + } + } + return; + }; + GutterKeyboardHandler.prototype.$moveAnnotationDown = function() { + var index = this.activeRowIndex; + while (index < this.lines.getLength() - 1) { + index++; + if (this.$isAnnotationVisible(index)) { + this.$blurAnnotation(this.activeRowIndex); + this.activeRowIndex = index; + this.$focusAnnotation(this.activeRowIndex); + return; + } + } + return; + }; + GutterKeyboardHandler.prototype.$switchLane = function(desinationLane) { + switch (desinationLane) { + case "annotation": + if (this.activeLane === "annotation") { + break; + } + var annotationIndex = this.$findNearestAnnotation(this.activeRowIndex); + if (annotationIndex == null) { + break; + } + this.activeLane = "annotation"; + this.$blurFoldWidget(this.activeRowIndex); + this.activeRowIndex = annotationIndex; + this.$focusAnnotation(this.activeRowIndex); + break; + case "fold": + if (this.activeLane === "fold") { + break; + } + var foldWidgetIndex = this.$findNearestFoldWidget(this.activeRowIndex); + if (foldWidgetIndex == null) { + break; + } + this.activeLane = "fold"; + this.$blurAnnotation(this.activeRowIndex); + this.activeRowIndex = foldWidgetIndex; + this.$focusFoldWidget(this.activeRowIndex); + break; + } + return; + }; + GutterKeyboardHandler.prototype.$rowIndexToRow = function(index) { + var cell = this.lines.get(index); + if (cell) + return cell.row; + return null; + }; + GutterKeyboardHandler.prototype.$rowToRowIndex = function(row) { + for (var i = 0; i < this.lines.getLength(); i++) { + var cell = this.lines.get(i); + if (cell.row == row) + return i; + } + return null; + }; + return GutterKeyboardHandler; + }()); + exports.GutterKeyboardHandler = GutterKeyboardHandler; + var GutterKeyboardEvent = /** @class */ (function() { + function GutterKeyboardEvent(domEvent, gutterKeyboardHandler) { + this.gutterKeyboardHandler = gutterKeyboardHandler; + this.domEvent = domEvent; + } + GutterKeyboardEvent.prototype.getKey = function() { + return keys.keyCodeToString(this.domEvent.keyCode); + }; + GutterKeyboardEvent.prototype.getRow = function() { + return this.gutterKeyboardHandler.$rowIndexToRow(this.gutterKeyboardHandler.activeRowIndex); + }; + GutterKeyboardEvent.prototype.isInAnnotationLane = function() { + return this.gutterKeyboardHandler.activeLane === "annotation"; + }; + GutterKeyboardEvent.prototype.isInFoldLane = function() { + return this.gutterKeyboardHandler.activeLane === "fold"; + }; + return GutterKeyboardEvent; + }()); + exports.GutterKeyboardEvent = GutterKeyboardEvent; + +}); + +define("ace/editor", ["require", "exports", "module", "ace/lib/oop", "ace/lib/dom", "ace/lib/lang", "ace/lib/useragent", "ace/keyboard/textinput", "ace/mouse/mouse_handler", "ace/mouse/fold_handler", "ace/keyboard/keybinding", "ace/edit_session", "ace/search", "ace/range", "ace/lib/event_emitter", "ace/commands/command_manager", "ace/commands/default_commands", "ace/config", "ace/token_iterator", "ace/line_widgets", "ace/keyboard/gutter_handler", "ace/config", "ace/clipboard", "ace/lib/keys"], function(require, exports, module) { + "use strict"; + var __values = (this && this.__values) || function(o) { + var s = typeof Symbol === "function" && Symbol.iterator, + m = s && o[s], + i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function() { + if (o && i >= o.length) o = void 0; + return { + value: o && o[i++], + done: !o + }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); + }; + var oop = require("./lib/oop"); + var dom = require("./lib/dom"); + var lang = require("./lib/lang"); + var useragent = require("./lib/useragent"); + var TextInput = require("./keyboard/textinput").TextInput; + var MouseHandler = require("./mouse/mouse_handler").MouseHandler; + var FoldHandler = require("./mouse/fold_handler").FoldHandler; + var KeyBinding = require("./keyboard/keybinding").KeyBinding; + var EditSession = require("./edit_session").EditSession; + var Search = require("./search").Search; + var Range = require("./range").Range; + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var CommandManager = require("./commands/command_manager").CommandManager; + var defaultCommands = require("./commands/default_commands").commands; + var config = require("./config"); + var TokenIterator = require("./token_iterator").TokenIterator; + var LineWidgets = require("./line_widgets").LineWidgets; + var GutterKeyboardHandler = require("./keyboard/gutter_handler").GutterKeyboardHandler; + var nls = require("./config").nls; + var clipboard = require("./clipboard"); + var keys = require('./lib/keys'); + var Editor = /** @class */ (function() { + function Editor(renderer, session, options) { + this.session; + this.$toDestroy = []; + var container = renderer.getContainerElement(); + this.container = container; + this.renderer = renderer; + this.id = "editor" + (++Editor.$uid); + this.commands = new CommandManager(useragent.isMac ? "mac" : "win", defaultCommands); + if (typeof document == "object") { + this.textInput = new TextInput(renderer.getTextAreaContainer(), this); + this.renderer.textarea = this.textInput.getElement(); + this.$mouseHandler = new MouseHandler(this); + new FoldHandler(this); + } + this.keyBinding = new KeyBinding(this); + this.$search = new Search().set({ + wrap: true + }); + this.$historyTracker = this.$historyTracker.bind(this); + this.commands.on("exec", this.$historyTracker); + this.$initOperationListeners(); + this._$emitInputEvent = lang.delayedCall(function() { + this._signal("input", {}); + if (this.session && !this.session.destroyed) + this.session.bgTokenizer.scheduleStart(); + }.bind(this)); + this.on("change", function(_, _self) { + _self._$emitInputEvent.schedule(31); + }); + this.setSession(session || options && options.session || new EditSession("")); + config.resetOptions(this); + if (options) + this.setOptions(options); + config._signal("editor", this); + } + Editor.prototype.$initOperationListeners = function() { + this.commands.on("exec", this.startOperation.bind(this), true); + this.commands.on("afterExec", this.endOperation.bind(this), true); + this.$opResetTimer = lang.delayedCall(this.endOperation.bind(this, true)); + this.on("change", function() { + if (!this.curOp) { + this.startOperation(); + this.curOp.selectionBefore = this.$lastSel; + } + this.curOp.docChanged = true; + }.bind(this), true); + this.on("changeSelection", function() { + if (!this.curOp) { + this.startOperation(); + this.curOp.selectionBefore = this.$lastSel; + } + this.curOp.selectionChanged = true; + }.bind(this), true); + }; + Editor.prototype.startOperation = function(commandEvent) { + if (this.curOp) { + if (!commandEvent || this.curOp.command) + return; + this.prevOp = this.curOp; + } + if (!commandEvent) { + this.previousCommand = null; + commandEvent = {}; + } + this.$opResetTimer.schedule(); + this.curOp = this.session.curOp = { + command: commandEvent.command || {}, + args: commandEvent.args, + scrollTop: this.renderer.scrollTop + }; + this.curOp.selectionBefore = this.selection.toJSON(); + }; + Editor.prototype.endOperation = function(e) { + if (this.curOp && this.session) { + if (e && e.returnValue === false || !this.session) + return (this.curOp = null); + if (e == true && this.curOp.command && this.curOp.command.name == "mouse") + return; + this._signal("beforeEndOperation"); + if (!this.curOp) + return; + var command = this.curOp.command; + var scrollIntoView = command && command.scrollIntoView; + if (scrollIntoView) { + switch (scrollIntoView) { + case "center-animate": + scrollIntoView = "animate"; + case "center": + this.renderer.scrollCursorIntoView(null, 0.5); + break; + case "animate": + case "cursor": + this.renderer.scrollCursorIntoView(); + break; + case "selectionPart": + var range = this.selection.getRange(); + var config = this.renderer.layerConfig; + if (range.start.row >= config.lastRow || range.end.row <= config.firstRow) { + this.renderer.scrollSelectionIntoView(this.selection.anchor, this.selection.lead); + } + break; + default: + break; + } + if (scrollIntoView == "animate") + this.renderer.animateScrolling(this.curOp.scrollTop); + } + var sel = this.selection.toJSON(); + this.curOp.selectionAfter = sel; + this.$lastSel = this.selection.toJSON(); + this.session.getUndoManager().addSelection(sel); + this.prevOp = this.curOp; + this.curOp = null; + } + }; + Editor.prototype.$historyTracker = function(e) { + if (!this.$mergeUndoDeltas) + return; + var prev = this.prevOp; + var mergeableCommands = this.$mergeableCommands; + var shouldMerge = prev.command && (e.command.name == prev.command.name); + if (e.command.name == "insertstring") { + var text = e.args; + if (this.mergeNextCommand === undefined) + this.mergeNextCommand = true; + shouldMerge = shouldMerge && + this.mergeNextCommand // previous command allows to coalesce with + && + (!/\s/.test(text) || /\s/.test(prev.args)); // previous insertion was of same type + this.mergeNextCommand = true; + } else { + shouldMerge = shouldMerge && + mergeableCommands.indexOf(e.command.name) !== -1; // the command is mergeable + } + if (this.$mergeUndoDeltas != "always" && + Date.now() - this.sequenceStartTime > 2000) { + shouldMerge = false; // the sequence is too long + } + if (shouldMerge) + this.session.mergeUndoDeltas = true; + else if (mergeableCommands.indexOf(e.command.name) !== -1) + this.sequenceStartTime = Date.now(); + }; + Editor.prototype.setKeyboardHandler = function(keyboardHandler, cb) { + if (keyboardHandler && typeof keyboardHandler === "string" && keyboardHandler != "ace") { + this.$keybindingId = keyboardHandler; + var _self = this; + config.loadModule(["keybinding", keyboardHandler], function(module) { + if (_self.$keybindingId == keyboardHandler) + _self.keyBinding.setKeyboardHandler(module && module.handler); + cb && cb(); + }); + } else { + this.$keybindingId = null; + this.keyBinding.setKeyboardHandler(keyboardHandler); + cb && cb(); + } + }; + Editor.prototype.getKeyboardHandler = function() { + return this.keyBinding.getKeyboardHandler(); + }; + Editor.prototype.setSession = function(session) { + if (this.session == session) + return; + if (this.curOp) + this.endOperation(); + this.curOp = {}; + var oldSession = this.session; + if (oldSession) { + this.session.off("change", this.$onDocumentChange); + this.session.off("changeMode", this.$onChangeMode); + this.session.off("tokenizerUpdate", this.$onTokenizerUpdate); + this.session.off("changeTabSize", this.$onChangeTabSize); + this.session.off("changeWrapLimit", this.$onChangeWrapLimit); + this.session.off("changeWrapMode", this.$onChangeWrapMode); + this.session.off("changeFold", this.$onChangeFold); + this.session.off("changeFrontMarker", this.$onChangeFrontMarker); + this.session.off("changeBackMarker", this.$onChangeBackMarker); + this.session.off("changeBreakpoint", this.$onChangeBreakpoint); + this.session.off("changeAnnotation", this.$onChangeAnnotation); + this.session.off("changeOverwrite", this.$onCursorChange); + this.session.off("changeScrollTop", this.$onScrollTopChange); + this.session.off("changeScrollLeft", this.$onScrollLeftChange); + var selection = this.session.getSelection(); + selection.off("changeCursor", this.$onCursorChange); + selection.off("changeSelection", this.$onSelectionChange); + } + this.session = session; + if (session) { + this.$onDocumentChange = this.onDocumentChange.bind(this); + session.on("change", this.$onDocumentChange); + this.renderer.setSession(session); + this.$onChangeMode = this.onChangeMode.bind(this); + session.on("changeMode", this.$onChangeMode); + this.$onTokenizerUpdate = this.onTokenizerUpdate.bind(this); + session.on("tokenizerUpdate", this.$onTokenizerUpdate); + this.$onChangeTabSize = this.renderer.onChangeTabSize.bind(this.renderer); + session.on("changeTabSize", this.$onChangeTabSize); + this.$onChangeWrapLimit = this.onChangeWrapLimit.bind(this); + session.on("changeWrapLimit", this.$onChangeWrapLimit); + this.$onChangeWrapMode = this.onChangeWrapMode.bind(this); + session.on("changeWrapMode", this.$onChangeWrapMode); + this.$onChangeFold = this.onChangeFold.bind(this); + session.on("changeFold", this.$onChangeFold); + this.$onChangeFrontMarker = this.onChangeFrontMarker.bind(this); + this.session.on("changeFrontMarker", this.$onChangeFrontMarker); + this.$onChangeBackMarker = this.onChangeBackMarker.bind(this); + this.session.on("changeBackMarker", this.$onChangeBackMarker); + this.$onChangeBreakpoint = this.onChangeBreakpoint.bind(this); + this.session.on("changeBreakpoint", this.$onChangeBreakpoint); + this.$onChangeAnnotation = this.onChangeAnnotation.bind(this); + this.session.on("changeAnnotation", this.$onChangeAnnotation); + this.$onCursorChange = this.onCursorChange.bind(this); + this.session.on("changeOverwrite", this.$onCursorChange); + this.$onScrollTopChange = this.onScrollTopChange.bind(this); + this.session.on("changeScrollTop", this.$onScrollTopChange); + this.$onScrollLeftChange = this.onScrollLeftChange.bind(this); + this.session.on("changeScrollLeft", this.$onScrollLeftChange); + this.selection = session.getSelection(); + this.selection.on("changeCursor", this.$onCursorChange); + this.$onSelectionChange = this.onSelectionChange.bind(this); + this.selection.on("changeSelection", this.$onSelectionChange); + this.onChangeMode(); + this.onCursorChange(); + this.onScrollTopChange(); + this.onScrollLeftChange(); + this.onSelectionChange(); + this.onChangeFrontMarker(); + this.onChangeBackMarker(); + this.onChangeBreakpoint(); + this.onChangeAnnotation(); + this.session.getUseWrapMode() && this.renderer.adjustWrapLimit(); + this.renderer.updateFull(); + } else { + this.selection = null; + this.renderer.setSession(session); + } + this._signal("changeSession", { + session: session, + oldSession: oldSession + }); + this.curOp = null; + oldSession && oldSession._signal("changeEditor", { + oldEditor: this + }); + session && session._signal("changeEditor", { + editor: this + }); + if (session && !session.destroyed) + session.bgTokenizer.scheduleStart(); + }; + Editor.prototype.getSession = function() { + return this.session; + }; + Editor.prototype.setValue = function(val, cursorPos) { + this.session.doc.setValue(val); + if (!cursorPos) + this.selectAll(); + else if (cursorPos == 1) + this.navigateFileEnd(); + else if (cursorPos == -1) + this.navigateFileStart(); + return val; + }; + Editor.prototype.getValue = function() { + return this.session.getValue(); + }; + Editor.prototype.getSelection = function() { + return this.selection; + }; + Editor.prototype.resize = function(force) { + this.renderer.onResize(force); + }; + Editor.prototype.setTheme = function(theme, cb) { + this.renderer.setTheme(theme, cb); + }; + Editor.prototype.getTheme = function() { + return this.renderer.getTheme(); + }; + Editor.prototype.setStyle = function(style) { + this.renderer.setStyle(style); + }; + Editor.prototype.unsetStyle = function(style) { + this.renderer.unsetStyle(style); + }; + Editor.prototype.getFontSize = function() { + return this.getOption("fontSize") || + dom.computedStyle(this.container).fontSize; + }; + Editor.prototype.setFontSize = function(size) { + this.setOption("fontSize", size); + }; + Editor.prototype.$highlightBrackets = function() { + if (this.$highlightPending) { + return; + } + var self = this; + this.$highlightPending = true; + setTimeout(function() { + self.$highlightPending = false; + var session = self.session; + if (!session || session.destroyed) + return; + if (session.$bracketHighlight) { + session.$bracketHighlight.markerIds.forEach(function(id) { + session.removeMarker(id); + }); + session.$bracketHighlight = null; + } + var pos = self.getCursorPosition(); + var handler = self.getKeyboardHandler(); + var isBackwards = handler && handler.$getDirectionForHighlight && handler.$getDirectionForHighlight(self); + var ranges = session.getMatchingBracketRanges(pos, isBackwards); + if (!ranges) { + var iterator = new TokenIterator(session, pos.row, pos.column); + var token = iterator.getCurrentToken(); + if (token && /\b(?:tag-open|tag-name)/.test(token.type)) { + var tagNamesRanges = session.getMatchingTags(pos); + if (tagNamesRanges) { + ranges = [ + tagNamesRanges.openTagName.isEmpty() ? tagNamesRanges.openTag : tagNamesRanges.openTagName, + tagNamesRanges.closeTagName.isEmpty() ? tagNamesRanges.closeTag : tagNamesRanges.closeTagName + ]; + } + } + } + if (!ranges && session.$mode.getMatching) + ranges = session.$mode.getMatching(self.session); + if (!ranges) { + if (self.getHighlightIndentGuides()) + self.renderer.$textLayer.$highlightIndentGuide(); + return; + } + var markerType = "ace_bracket"; + if (!Array.isArray(ranges)) { + ranges = [ranges]; + } else if (ranges.length == 1) { + markerType = "ace_error_bracket"; + } + if (ranges.length == 2) { + if (Range.comparePoints(ranges[0].end, ranges[1].start) == 0) + ranges = [Range.fromPoints(ranges[0].start, ranges[1].end)]; + else if (Range.comparePoints(ranges[0].start, ranges[1].end) == 0) + ranges = [Range.fromPoints(ranges[1].start, ranges[0].end)]; + } + session.$bracketHighlight = { + ranges: ranges, + markerIds: ranges.map(function(range) { + return session.addMarker(range, markerType, "text"); + }) + }; + if (self.getHighlightIndentGuides()) + self.renderer.$textLayer.$highlightIndentGuide(); + }, 50); + }; + Editor.prototype.focus = function() { + this.textInput.focus(); + }; + Editor.prototype.isFocused = function() { + return this.textInput.isFocused(); + }; + Editor.prototype.blur = function() { + this.textInput.blur(); + }; + Editor.prototype.onFocus = function(e) { + if (this.$isFocused) + return; + this.$isFocused = true; + this.renderer.showCursor(); + this.renderer.visualizeFocus(); + this._emit("focus", e); + }; + Editor.prototype.onBlur = function(e) { + if (!this.$isFocused) + return; + this.$isFocused = false; + this.renderer.hideCursor(); + this.renderer.visualizeBlur(); + this._emit("blur", e); + }; + Editor.prototype.$cursorChange = function() { + this.renderer.updateCursor(); + this.$highlightBrackets(); + this.$updateHighlightActiveLine(); + }; + Editor.prototype.onDocumentChange = function(delta) { + var wrap = this.session.$useWrapMode; + var lastRow = (delta.start.row == delta.end.row ? delta.end.row : Infinity); + this.renderer.updateLines(delta.start.row, lastRow, wrap); + this._signal("change", delta); + this.$cursorChange(); + }; + Editor.prototype.onTokenizerUpdate = function(e) { + var rows = e.data; + this.renderer.updateLines(rows.first, rows.last); + }; + Editor.prototype.onScrollTopChange = function() { + this.renderer.scrollToY(this.session.getScrollTop()); + }; + Editor.prototype.onScrollLeftChange = function() { + this.renderer.scrollToX(this.session.getScrollLeft()); + }; + Editor.prototype.onCursorChange = function() { + this.$cursorChange(); + this._signal("changeSelection"); + }; + Editor.prototype.$updateHighlightActiveLine = function() { + var session = this.getSession(); + var highlight; + if (this.$highlightActiveLine) { + if (this.$selectionStyle != "line" || !this.selection.isMultiLine()) + highlight = this.getCursorPosition(); + if (this.renderer.theme && this.renderer.theme.$selectionColorConflict && !this.selection.isEmpty()) + highlight = false; + if (this.renderer.$maxLines && this.session.getLength() === 1 && !(this.renderer.$minLines > 1)) + highlight = false; + } + if (session.$highlightLineMarker && !highlight) { + session.removeMarker(session.$highlightLineMarker.id); + session.$highlightLineMarker = null; + } else if (!session.$highlightLineMarker && highlight) { + var range = new Range(highlight.row, highlight.column, highlight.row, Infinity); + range.id = session.addMarker(range, "ace_active-line", "screenLine"); + session.$highlightLineMarker = range; + } else if (highlight) { + session.$highlightLineMarker.start.row = highlight.row; + session.$highlightLineMarker.end.row = highlight.row; + session.$highlightLineMarker.start.column = highlight.column; + session._signal("changeBackMarker"); + } + }; + Editor.prototype.onSelectionChange = function(e) { + var session = this.session; + if (session.$selectionMarker) { + session.removeMarker(session.$selectionMarker); + } + session.$selectionMarker = null; + if (!this.selection.isEmpty()) { + var range = this.selection.getRange(); + var style = this.getSelectionStyle(); + session.$selectionMarker = session.addMarker(range, "ace_selection", style); + } else { + this.$updateHighlightActiveLine(); + } + var re = this.$highlightSelectedWord && this.$getSelectionHighLightRegexp(); + this.session.highlight(re); + this._signal("changeSelection"); + }; + Editor.prototype.$getSelectionHighLightRegexp = function() { + var session = this.session; + var selection = this.getSelectionRange(); + if (selection.isEmpty() || selection.isMultiLine()) + return; + var startColumn = selection.start.column; + var endColumn = selection.end.column; + var line = session.getLine(selection.start.row); + var needle = line.substring(startColumn, endColumn); + if (needle.length > 5000 || !/[\w\d]/.test(needle)) + return; + var re = this.$search.$assembleRegExp({ + wholeWord: true, + caseSensitive: true, + needle: needle + }); + var wordWithBoundary = line.substring(startColumn - 1, endColumn + 1); + if (!re.test(wordWithBoundary)) + return; + return re; + }; + Editor.prototype.onChangeFrontMarker = function() { + this.renderer.updateFrontMarkers(); + }; + Editor.prototype.onChangeBackMarker = function() { + this.renderer.updateBackMarkers(); + }; + Editor.prototype.onChangeBreakpoint = function() { + this.renderer.updateBreakpoints(); + }; + Editor.prototype.onChangeAnnotation = function() { + this.renderer.setAnnotations(this.session.getAnnotations()); + }; + Editor.prototype.onChangeMode = function(e) { + this.renderer.updateText(); + this._emit("changeMode", e); + }; + Editor.prototype.onChangeWrapLimit = function() { + this.renderer.updateFull(); + }; + Editor.prototype.onChangeWrapMode = function() { + this.renderer.onResize(true); + }; + Editor.prototype.onChangeFold = function() { + this.$updateHighlightActiveLine(); + this.renderer.updateFull(); + }; + Editor.prototype.getSelectedText = function() { + return this.session.getTextRange(this.getSelectionRange()); + }; + Editor.prototype.getCopyText = function() { + var text = this.getSelectedText(); + var nl = this.session.doc.getNewLineCharacter(); + var copyLine = false; + if (!text && this.$copyWithEmptySelection) { + copyLine = true; + var ranges = this.selection.getAllRanges(); + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (i && ranges[i - 1].start.row == range.start.row) + continue; + text += this.session.getLine(range.start.row) + nl; + } + } + var e = { + text: text + }; + this._signal("copy", e); + clipboard.lineMode = copyLine ? e.text : false; + return e.text; + }; + Editor.prototype.onCopy = function() { + this.commands.exec("copy", this); + }; + Editor.prototype.onCut = function() { + this.commands.exec("cut", this); + }; + Editor.prototype.onPaste = function(text, event) { + var e = { + text: text, + event: event + }; + this.commands.exec("paste", this, e); + }; + Editor.prototype.$handlePaste = function(e) { + if (typeof e == "string") + e = { + text: e + }; + this._signal("paste", e); + var text = e.text; + var lineMode = text === clipboard.lineMode; + var session = this.session; + if (!this.inMultiSelectMode || this.inVirtualSelectionMode) { + if (lineMode) + session.insert({ + row: this.selection.lead.row, + column: 0 + }, text); + else + this.insert(text); + } else if (lineMode) { + this.selection.rangeList.ranges.forEach(function(range) { + session.insert({ + row: range.start.row, + column: 0 + }, text); + }); + } else { + var lines = text.split(/\r\n|\r|\n/); + var ranges = this.selection.rangeList.ranges; + var isFullLine = lines.length == 2 && (!lines[0] || !lines[1]); + if (lines.length != ranges.length || isFullLine) + return this.commands.exec("insertstring", this, text); + for (var i = ranges.length; i--;) { + var range = ranges[i]; + if (!range.isEmpty()) + session.remove(range); + session.insert(range.start, lines[i]); + } + } + }; + Editor.prototype.execCommand = function(command, args) { + return this.commands.exec(command, this, args); + }; + Editor.prototype.insert = function(text, pasted) { + var session = this.session; + var mode = session.getMode(); + var cursor = this.getCursorPosition(); + if (this.getBehavioursEnabled() && !pasted) { + var transform = mode.transformAction(session.getState(cursor.row), 'insertion', this, session, text); + if (transform) { + if (text !== transform.text) { + if (!this.inVirtualSelectionMode) { + this.session.mergeUndoDeltas = false; + this.mergeNextCommand = false; + } + } + text = transform.text; + } + } + if (text == "\t") + text = this.session.getTabString(); + if (!this.selection.isEmpty()) { + var range = this.getSelectionRange(); + cursor = this.session.remove(range); + this.clearSelection(); + } else if (this.session.getOverwrite() && text.indexOf("\n") == -1) { + var range = Range.fromPoints(cursor, cursor); + range.end.column += text.length; + this.session.remove(range); + } + if (text == "\n" || text == "\r\n") { + var line = session.getLine(cursor.row); + if (cursor.column > line.search(/\S|$/)) { + var d = line.substr(cursor.column).search(/\S|$/); + session.doc.removeInLine(cursor.row, cursor.column, cursor.column + d); + } + } + this.clearSelection(); + var start = cursor.column; + var lineState = session.getState(cursor.row); + var line = session.getLine(cursor.row); + var shouldOutdent = mode.checkOutdent(lineState, line, text); + session.insert(cursor, text); + if (transform && transform.selection) { + if (transform.selection.length == 2) { // Transform relative to the current column + this.selection.setSelectionRange(new Range(cursor.row, start + transform.selection[0], cursor.row, start + transform.selection[1])); + } else { // Transform relative to the current row. + this.selection.setSelectionRange(new Range(cursor.row + transform.selection[0], transform.selection[1], cursor.row + transform.selection[2], transform.selection[3])); + } + } + if (this.$enableAutoIndent) { + if (session.getDocument().isNewLine(text)) { + var lineIndent = mode.getNextLineIndent(lineState, line.slice(0, cursor.column), session.getTabString()); + session.insert({ + row: cursor.row + 1, + column: 0 + }, lineIndent); + } + if (shouldOutdent) + mode.autoOutdent(lineState, session, cursor.row); + } + }; + Editor.prototype.autoIndent = function() { + var session = this.session; + var mode = session.getMode(); + var ranges = this.selection.isEmpty() ? [new Range(0, 0, session.doc.getLength() - 1, 0)] : + this.selection.getAllRanges(); + var prevLineState = ""; + var prevLine = ""; + var lineIndent = ""; + var tab = session.getTabString(); + for (var i = 0; i < ranges.length; i++) { + var startRow = ranges[i].start.row; + var endRow = ranges[i].end.row; + for (var row = startRow; row <= endRow; row++) { + if (row > 0) { + prevLineState = session.getState(row - 1); + prevLine = session.getLine(row - 1); + lineIndent = mode.getNextLineIndent(prevLineState, prevLine, tab); + } + var line = session.getLine(row); + var currIndent = mode.$getIndent(line); + if (lineIndent !== currIndent) { + if (currIndent.length > 0) { + var range = new Range(row, 0, row, currIndent.length); + session.remove(range); + } + if (lineIndent.length > 0) { + session.insert({ + row: row, + column: 0 + }, lineIndent); + } + } + mode.autoOutdent(prevLineState, session, row); + } + } + }; + Editor.prototype.onTextInput = function(text, composition) { + if (!composition) + return this.keyBinding.onTextInput(text); + this.startOperation({ + command: { + name: "insertstring" + } + }); + var applyComposition = this.applyComposition.bind(this, text, composition); + if (this.selection.rangeCount) + this.forEachSelection(applyComposition); + else + applyComposition(); + this.endOperation(); + }; + Editor.prototype.applyComposition = function(text, composition) { + if (composition.extendLeft || composition.extendRight) { + var r = this.selection.getRange(); + r.start.column -= composition.extendLeft; + r.end.column += composition.extendRight; + if (r.start.column < 0) { + r.start.row--; + r.start.column += this.session.getLine(r.start.row).length + 1; + } + this.selection.setRange(r); + if (!text && !r.isEmpty()) + this.remove(); + } + if (text || !this.selection.isEmpty()) + this.insert(text, true); + if (composition.restoreStart || composition.restoreEnd) { + var r = this.selection.getRange(); + r.start.column -= composition.restoreStart; + r.end.column -= composition.restoreEnd; + this.selection.setRange(r); + } + }; + Editor.prototype.onCommandKey = function(e, hashId, keyCode) { + return this.keyBinding.onCommandKey(e, hashId, keyCode); + }; + Editor.prototype.setOverwrite = function(overwrite) { + this.session.setOverwrite(overwrite); + }; + Editor.prototype.getOverwrite = function() { + return this.session.getOverwrite(); + }; + Editor.prototype.toggleOverwrite = function() { + this.session.toggleOverwrite(); + }; + Editor.prototype.setScrollSpeed = function(speed) { + this.setOption("scrollSpeed", speed); + }; + Editor.prototype.getScrollSpeed = function() { + return this.getOption("scrollSpeed"); + }; + Editor.prototype.setDragDelay = function(dragDelay) { + this.setOption("dragDelay", dragDelay); + }; + Editor.prototype.getDragDelay = function() { + return this.getOption("dragDelay"); + }; + Editor.prototype.setSelectionStyle = function(val) { + this.setOption("selectionStyle", val); + }; + Editor.prototype.getSelectionStyle = function() { + return this.getOption("selectionStyle"); + }; + Editor.prototype.setHighlightActiveLine = function(shouldHighlight) { + this.setOption("highlightActiveLine", shouldHighlight); + }; + Editor.prototype.getHighlightActiveLine = function() { + return this.getOption("highlightActiveLine"); + }; + Editor.prototype.setHighlightGutterLine = function(shouldHighlight) { + this.setOption("highlightGutterLine", shouldHighlight); + }; + Editor.prototype.getHighlightGutterLine = function() { + return this.getOption("highlightGutterLine"); + }; + Editor.prototype.setHighlightSelectedWord = function(shouldHighlight) { + this.setOption("highlightSelectedWord", shouldHighlight); + }; + Editor.prototype.getHighlightSelectedWord = function() { + return this.$highlightSelectedWord; + }; + Editor.prototype.setAnimatedScroll = function(shouldAnimate) { + this.renderer.setAnimatedScroll(shouldAnimate); + }; + Editor.prototype.getAnimatedScroll = function() { + return this.renderer.getAnimatedScroll(); + }; + Editor.prototype.setShowInvisibles = function(showInvisibles) { + this.renderer.setShowInvisibles(showInvisibles); + }; + Editor.prototype.getShowInvisibles = function() { + return this.renderer.getShowInvisibles(); + }; + Editor.prototype.setDisplayIndentGuides = function(display) { + this.renderer.setDisplayIndentGuides(display); + }; + Editor.prototype.getDisplayIndentGuides = function() { + return this.renderer.getDisplayIndentGuides(); + }; + Editor.prototype.setHighlightIndentGuides = function(highlight) { + this.renderer.setHighlightIndentGuides(highlight); + }; + Editor.prototype.getHighlightIndentGuides = function() { + return this.renderer.getHighlightIndentGuides(); + }; + Editor.prototype.setShowPrintMargin = function(showPrintMargin) { + this.renderer.setShowPrintMargin(showPrintMargin); + }; + Editor.prototype.getShowPrintMargin = function() { + return this.renderer.getShowPrintMargin(); + }; + Editor.prototype.setPrintMarginColumn = function(showPrintMargin) { + this.renderer.setPrintMarginColumn(showPrintMargin); + }; + Editor.prototype.getPrintMarginColumn = function() { + return this.renderer.getPrintMarginColumn(); + }; + Editor.prototype.setReadOnly = function(readOnly) { + this.setOption("readOnly", readOnly); + }; + Editor.prototype.getReadOnly = function() { + return this.getOption("readOnly"); + }; + Editor.prototype.setBehavioursEnabled = function(enabled) { + this.setOption("behavioursEnabled", enabled); + }; + Editor.prototype.getBehavioursEnabled = function() { + return this.getOption("behavioursEnabled"); + }; + Editor.prototype.setWrapBehavioursEnabled = function(enabled) { + this.setOption("wrapBehavioursEnabled", enabled); + }; + Editor.prototype.getWrapBehavioursEnabled = function() { + return this.getOption("wrapBehavioursEnabled"); + }; + Editor.prototype.setShowFoldWidgets = function(show) { + this.setOption("showFoldWidgets", show); + }; + Editor.prototype.getShowFoldWidgets = function() { + return this.getOption("showFoldWidgets"); + }; + Editor.prototype.setFadeFoldWidgets = function(fade) { + this.setOption("fadeFoldWidgets", fade); + }; + Editor.prototype.getFadeFoldWidgets = function() { + return this.getOption("fadeFoldWidgets"); + }; + Editor.prototype.remove = function(dir) { + if (this.selection.isEmpty()) { + if (dir == "left") + this.selection.selectLeft(); + else + this.selection.selectRight(); + } + var range = this.getSelectionRange(); + if (this.getBehavioursEnabled()) { + var session = this.session; + var state = session.getState(range.start.row); + var new_range = session.getMode().transformAction(state, 'deletion', this, session, range); + if (range.end.column === 0) { + var text = session.getTextRange(range); + if (text[text.length - 1] == "\n") { + var line = session.getLine(range.end.row); + if (/^\s+$/.test(line)) { + range.end.column = line.length; + } + } + } + if (new_range) + range = new_range; + } + this.session.remove(range); + this.clearSelection(); + }; + Editor.prototype.removeWordRight = function() { + if (this.selection.isEmpty()) + this.selection.selectWordRight(); + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + Editor.prototype.removeWordLeft = function() { + if (this.selection.isEmpty()) + this.selection.selectWordLeft(); + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + Editor.prototype.removeToLineStart = function() { + if (this.selection.isEmpty()) + this.selection.selectLineStart(); + if (this.selection.isEmpty()) + this.selection.selectLeft(); + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + }; + Editor.prototype.removeToLineEnd = function() { + if (this.selection.isEmpty()) + this.selection.selectLineEnd(); + var range = this.getSelectionRange(); + if (range.start.column == range.end.column && range.start.row == range.end.row) { + range.end.column = 0; + range.end.row++; + } + this.session.remove(range); + this.clearSelection(); + }; + Editor.prototype.splitLine = function() { + if (!this.selection.isEmpty()) { + this.session.remove(this.getSelectionRange()); + this.clearSelection(); + } + var cursor = this.getCursorPosition(); + this.insert("\n"); + this.moveCursorToPosition(cursor); + }; + Editor.prototype.setGhostText = function(text, position) { + if (!this.session.widgetManager) { + this.session.widgetManager = new LineWidgets(this.session); + this.session.widgetManager.attach(this); + } + this.renderer.setGhostText(text, position); + }; + Editor.prototype.removeGhostText = function() { + if (!this.session.widgetManager) + return; + this.renderer.removeGhostText(); + }; + Editor.prototype.transposeLetters = function() { + if (!this.selection.isEmpty()) { + return; + } + var cursor = this.getCursorPosition(); + var column = cursor.column; + if (column === 0) + return; + var line = this.session.getLine(cursor.row); + var swap, range; + if (column < line.length) { + swap = line.charAt(column) + line.charAt(column - 1); + range = new Range(cursor.row, column - 1, cursor.row, column + 1); + } else { + swap = line.charAt(column - 1) + line.charAt(column - 2); + range = new Range(cursor.row, column - 2, cursor.row, column); + } + this.session.replace(range, swap); + this.session.selection.moveToPosition(range.end); + }; + Editor.prototype.toLowerCase = function() { + var originalRange = this.getSelectionRange(); + if (this.selection.isEmpty()) { + this.selection.selectWord(); + } + var range = this.getSelectionRange(); + var text = this.session.getTextRange(range); + this.session.replace(range, text.toLowerCase()); + this.selection.setSelectionRange(originalRange); + }; + Editor.prototype.toUpperCase = function() { + var originalRange = this.getSelectionRange(); + if (this.selection.isEmpty()) { + this.selection.selectWord(); + } + var range = this.getSelectionRange(); + var text = this.session.getTextRange(range); + this.session.replace(range, text.toUpperCase()); + this.selection.setSelectionRange(originalRange); + }; + Editor.prototype.indent = function() { + var session = this.session; + var range = this.getSelectionRange(); + if (range.start.row < range.end.row) { + var rows = this.$getSelectedRows(); + session.indentRows(rows.first, rows.last, "\t"); + return; + } else if (range.start.column < range.end.column) { + var text = session.getTextRange(range); + if (!/^\s+$/.test(text)) { + var rows = this.$getSelectedRows(); + session.indentRows(rows.first, rows.last, "\t"); + return; + } + } + var line = session.getLine(range.start.row); + var position = range.start; + var size = session.getTabSize(); + var column = session.documentToScreenColumn(position.row, position.column); + if (this.session.getUseSoftTabs()) { + var count = (size - column % size); + var indentString = lang.stringRepeat(" ", count); + } else { + var count = column % size; + while (line[range.start.column - 1] == " " && count) { + range.start.column--; + count--; + } + this.selection.setSelectionRange(range); + indentString = "\t"; + } + return this.insert(indentString); + }; + Editor.prototype.blockIndent = function() { + var rows = this.$getSelectedRows(); + this.session.indentRows(rows.first, rows.last, "\t"); + }; + Editor.prototype.blockOutdent = function() { + var selection = this.session.getSelection(); + this.session.outdentRows(selection.getRange()); + }; + Editor.prototype.sortLines = function() { + var rows = this.$getSelectedRows(); + var session = this.session; + var lines = []; + for (var i = rows.first; i <= rows.last; i++) + lines.push(session.getLine(i)); + lines.sort(function(a, b) { + if (a.toLowerCase() < b.toLowerCase()) + return -1; + if (a.toLowerCase() > b.toLowerCase()) + return 1; + return 0; + }); + var deleteRange = new Range(0, 0, 0, 0); + for (var i = rows.first; i <= rows.last; i++) { + var line = session.getLine(i); + deleteRange.start.row = i; + deleteRange.end.row = i; + deleteRange.end.column = line.length; + session.replace(deleteRange, lines[i - rows.first]); + } + }; + Editor.prototype.toggleCommentLines = function() { + var state = this.session.getState(this.getCursorPosition().row); + var rows = this.$getSelectedRows(); + this.session.getMode().toggleCommentLines(state, this.session, rows.first, rows.last); + }; + Editor.prototype.toggleBlockComment = function() { + var cursor = this.getCursorPosition(); + var state = this.session.getState(cursor.row); + var range = this.getSelectionRange(); + this.session.getMode().toggleBlockComment(state, this.session, range, cursor); + }; + Editor.prototype.getNumberAt = function(row, column) { + var _numberRx = /[\-]?[0-9]+(?:\.[0-9]+)?/g; + _numberRx.lastIndex = 0; + var s = this.session.getLine(row); + while (_numberRx.lastIndex < column) { + var m = _numberRx.exec(s); + if (m.index <= column && m.index + m[0].length >= column) { + var number = { + value: m[0], + start: m.index, + end: m.index + m[0].length + }; + return number; + } + } + return null; + }; + Editor.prototype.modifyNumber = function(amount) { + var row = this.selection.getCursor().row; + var column = this.selection.getCursor().column; + var charRange = new Range(row, column - 1, row, column); + var c = this.session.getTextRange(charRange); + if (!isNaN(parseFloat(c)) && isFinite(c)) { + var nr = this.getNumberAt(row, column); + if (nr) { + var fp = nr.value.indexOf(".") >= 0 ? nr.start + nr.value.indexOf(".") + 1 : nr.end; + var decimals = nr.start + nr.value.length - fp; + var t = parseFloat(nr.value); + t *= Math.pow(10, decimals); + if (fp !== nr.end && column < fp) { + amount *= Math.pow(10, nr.end - column - 1); + } else { + amount *= Math.pow(10, nr.end - column); + } + t += amount; + t /= Math.pow(10, decimals); + var nnr = t.toFixed(decimals); + var replaceRange = new Range(row, nr.start, row, nr.end); + this.session.replace(replaceRange, nnr); + this.moveCursorTo(row, Math.max(nr.start + 1, column + nnr.length - nr.value.length)); + } + } else { + this.toggleWord(); + } + }; + Editor.prototype.toggleWord = function() { + var row = this.selection.getCursor().row; + var column = this.selection.getCursor().column; + this.selection.selectWord(); + var currentState = this.getSelectedText(); + var currWordStart = this.selection.getWordRange().start.column; + var wordParts = currentState.replace(/([a-z]+|[A-Z]+)(?=[A-Z_]|$)/g, '$1 ').split(/\s/); + var delta = column - currWordStart - 1; + if (delta < 0) + delta = 0; + var curLength = 0, + itLength = 0; + var that = this; + if (currentState.match(/[A-Za-z0-9_]+/)) { + wordParts.forEach(function(item, i) { + itLength = curLength + item.length; + if (delta >= curLength && delta <= itLength) { + currentState = item; + that.selection.clearSelection(); + that.moveCursorTo(row, curLength + currWordStart); + that.selection.selectTo(row, itLength + currWordStart); + } + curLength = itLength; + }); + } + var wordPairs = this.$toggleWordPairs; + var reg; + for (var i = 0; i < wordPairs.length; i++) { + var item = wordPairs[i]; + for (var j = 0; j <= 1; j++) { + var negate = +!j; + var firstCondition = currentState.match(new RegExp('^\\s?_?(' + lang.escapeRegExp(item[j]) + ')\\s?$', 'i')); + if (firstCondition) { + var secondCondition = currentState.match(new RegExp('([_]|^|\\s)(' + lang.escapeRegExp(firstCondition[1]) + ')($|\\s)', 'g')); + if (secondCondition) { + reg = currentState.replace(new RegExp(lang.escapeRegExp(item[j]), 'i'), function(result) { + var res = item[negate]; + if (result.toUpperCase() == result) { + res = res.toUpperCase(); + } else if (result.charAt(0).toUpperCase() == result.charAt(0)) { + res = res.substr(0, 0) + item[negate].charAt(0).toUpperCase() + res.substr(1); + } + return res; + }); + this.insert(reg); + reg = ""; + } + } + } + } + }; + Editor.prototype.findLinkAt = function(row, column) { + var e_1, _a; + var line = this.session.getLine(row); + var wordParts = line.split(/((?:https?|ftp):\/\/[\S]+)/); + var columnPosition = column; + if (columnPosition < 0) + columnPosition = 0; + var previousPosition = 0, + currentPosition = 0, + match; + try { + for (var wordParts_1 = __values(wordParts), wordParts_1_1 = wordParts_1.next(); !wordParts_1_1.done; wordParts_1_1 = wordParts_1.next()) { + var item = wordParts_1_1.value; + currentPosition = previousPosition + item.length; + if (columnPosition >= previousPosition && columnPosition <= currentPosition) { + if (item.match(/((?:https?|ftp):\/\/[\S]+)/)) { + match = item.replace(/[\s:.,'";}\]]+$/, ""); + break; + } + } + previousPosition = currentPosition; + } + } catch (e_1_1) { + e_1 = { + error: e_1_1 + }; + } finally { + try { + if (wordParts_1_1 && !wordParts_1_1.done && (_a = wordParts_1.return)) _a.call(wordParts_1); + } finally { + if (e_1) throw e_1.error; + } + } + return match; + }; + Editor.prototype.openLink = function() { + var cursor = this.selection.getCursor(); + var url = this.findLinkAt(cursor.row, cursor.column); + if (url) + window.open(url, '_blank'); + return url != null; + }; + Editor.prototype.removeLines = function() { + var rows = this.$getSelectedRows(); + this.session.removeFullLines(rows.first, rows.last); + this.clearSelection(); + }; + Editor.prototype.duplicateSelection = function() { + var sel = this.selection; + var doc = this.session; + var range = sel.getRange(); + var reverse = sel.isBackwards(); + if (range.isEmpty()) { + var row = range.start.row; + doc.duplicateLines(row, row); + } else { + var point = reverse ? range.start : range.end; + var endPoint = doc.insert(point, doc.getTextRange(range)); + range.start = point; + range.end = endPoint; + sel.setSelectionRange(range, reverse); + } + }; + Editor.prototype.moveLinesDown = function() { + this.$moveLines(1, false); + }; + Editor.prototype.moveLinesUp = function() { + this.$moveLines(-1, false); + }; + Editor.prototype.moveText = function(range, toPosition, copy) { + return this.session.moveText(range, toPosition, copy); + }; + Editor.prototype.copyLinesUp = function() { + this.$moveLines(-1, true); + }; + Editor.prototype.copyLinesDown = function() { + this.$moveLines(1, true); + }; + Editor.prototype.$moveLines = function(dir, copy) { + var rows, moved; + var selection = this.selection; + if (!selection.inMultiSelectMode || this.inVirtualSelectionMode) { + var range = selection.toOrientedRange(); + rows = this.$getSelectedRows(range); + moved = this.session.$moveLines(rows.first, rows.last, copy ? 0 : dir); + if (copy && dir == -1) + moved = 0; + range.moveBy(moved, 0); + selection.fromOrientedRange(range); + } else { + var ranges = selection.rangeList.ranges; + selection.rangeList.detach(this.session); + this.inVirtualSelectionMode = true; + var diff = 0; + var totalDiff = 0; + var l = ranges.length; + for (var i = 0; i < l; i++) { + var rangeIndex = i; + ranges[i].moveBy(diff, 0); + rows = this.$getSelectedRows(ranges[i]); + var first = rows.first; + var last = rows.last; + while (++i < l) { + if (totalDiff) + ranges[i].moveBy(totalDiff, 0); + var subRows = this.$getSelectedRows(ranges[i]); + if (copy && subRows.first != last) + break; + else if (!copy && subRows.first > last + 1) + break; + last = subRows.last; + } + i--; + diff = this.session.$moveLines(first, last, copy ? 0 : dir); + if (copy && dir == -1) + rangeIndex = i + 1; + while (rangeIndex <= i) { + ranges[rangeIndex].moveBy(diff, 0); + rangeIndex++; + } + if (!copy) + diff = 0; + totalDiff += diff; + } + selection.fromOrientedRange(selection.ranges[0]); + selection.rangeList.attach(this.session); + this.inVirtualSelectionMode = false; + } + }; + Editor.prototype.$getSelectedRows = function(range) { + range = (range || this.getSelectionRange()).collapseRows(); + return { + first: this.session.getRowFoldStart(range.start.row), + last: this.session.getRowFoldEnd(range.end.row) + }; + }; + Editor.prototype.onCompositionStart = function(compositionState) { + this.renderer.showComposition(compositionState); + }; + Editor.prototype.onCompositionUpdate = function(text) { + this.renderer.setCompositionText(text); + }; + Editor.prototype.onCompositionEnd = function() { + this.renderer.hideComposition(); + }; + Editor.prototype.getFirstVisibleRow = function() { + return this.renderer.getFirstVisibleRow(); + }; + Editor.prototype.getLastVisibleRow = function() { + return this.renderer.getLastVisibleRow(); + }; + Editor.prototype.isRowVisible = function(row) { + return (row >= this.getFirstVisibleRow() && row <= this.getLastVisibleRow()); + }; + Editor.prototype.isRowFullyVisible = function(row) { + return (row >= this.renderer.getFirstFullyVisibleRow() && row <= this.renderer.getLastFullyVisibleRow()); + }; + Editor.prototype.$getVisibleRowCount = function() { + return this.renderer.getScrollBottomRow() - this.renderer.getScrollTopRow() + 1; + }; + Editor.prototype.$moveByPage = function(dir, select) { + var renderer = this.renderer; + var config = this.renderer.layerConfig; + var rows = dir * Math.floor(config.height / config.lineHeight); + if (select === true) { + this.selection.$moveSelection(function() { + this.moveCursorBy(rows, 0); + }); + } else if (select === false) { + this.selection.moveCursorBy(rows, 0); + this.selection.clearSelection(); + } + var scrollTop = renderer.scrollTop; + renderer.scrollBy(0, rows * config.lineHeight); + if (select != null) + renderer.scrollCursorIntoView(null, 0.5); + renderer.animateScrolling(scrollTop); + }; + Editor.prototype.selectPageDown = function() { + this.$moveByPage(1, true); + }; + Editor.prototype.selectPageUp = function() { + this.$moveByPage(-1, true); + }; + Editor.prototype.gotoPageDown = function() { + this.$moveByPage(1, false); + }; + Editor.prototype.gotoPageUp = function() { + this.$moveByPage(-1, false); + }; + Editor.prototype.scrollPageDown = function() { + this.$moveByPage(1); + }; + Editor.prototype.scrollPageUp = function() { + this.$moveByPage(-1); + }; + Editor.prototype.scrollToRow = function(row) { + this.renderer.scrollToRow(row); + }; + Editor.prototype.scrollToLine = function(line, center, animate, callback) { + this.renderer.scrollToLine(line, center, animate, callback); + }; + Editor.prototype.centerSelection = function() { + var range = this.getSelectionRange(); + var pos = { + row: Math.floor(range.start.row + (range.end.row - range.start.row) / 2), + column: Math.floor(range.start.column + (range.end.column - range.start.column) / 2) + }; + this.renderer.alignCursor(pos, 0.5); + }; + Editor.prototype.getCursorPosition = function() { + return this.selection.getCursor(); + }; + Editor.prototype.getCursorPositionScreen = function() { + return this.session.documentToScreenPosition(this.getCursorPosition()); + }; + Editor.prototype.getSelectionRange = function() { + return this.selection.getRange(); + }; + Editor.prototype.selectAll = function() { + this.selection.selectAll(); + }; + Editor.prototype.clearSelection = function() { + this.selection.clearSelection(); + }; + Editor.prototype.moveCursorTo = function(row, column) { + this.selection.moveCursorTo(row, column); + }; + Editor.prototype.moveCursorToPosition = function(pos) { + this.selection.moveCursorToPosition(pos); + }; + Editor.prototype.jumpToMatching = function(select, expand) { + var cursor = this.getCursorPosition(); + var iterator = new TokenIterator(this.session, cursor.row, cursor.column); + var prevToken = iterator.getCurrentToken(); + var tokenCount = 0; + if (prevToken && prevToken.type.indexOf('tag-name') !== -1) { + prevToken = iterator.stepBackward(); + } + var token = prevToken || iterator.stepForward(); + if (!token) + return; + var matchType; + var found = false; + var depth = {}; + var i = cursor.column - token.start; + var bracketType; + var brackets = { + ")": "(", + "(": "(", + "]": "[", + "[": "[", + "{": "{", + "}": "{" + }; + do { + if (token.value.match(/[{}()\[\]]/g)) { + for (; i < token.value.length && !found; i++) { + if (!brackets[token.value[i]]) { + continue; + } + bracketType = brackets[token.value[i]] + '.' + token.type.replace("rparen", "lparen"); + if (isNaN(depth[bracketType])) { + depth[bracketType] = 0; + } + switch (token.value[i]) { + case '(': + case '[': + case '{': + depth[bracketType]++; + break; + case ')': + case ']': + case '}': + depth[bracketType]--; + if (depth[bracketType] === -1) { + matchType = 'bracket'; + found = true; + } + break; + } + } + } else if (token.type.indexOf('tag-name') !== -1) { + if (isNaN(depth[token.value])) { + depth[token.value] = 0; + } + if (prevToken.value === '<' && tokenCount > 1) { + depth[token.value]++; + } else if (prevToken.value === '= 0; --i) { + if (this.$tryReplace(ranges[i], replacement)) { + replaced++; + } + } + this.selection.setSelectionRange(selection); + return replaced; + }; + Editor.prototype.$tryReplace = function(range, replacement) { + var input = this.session.getTextRange(range); + replacement = this.$search.replace(input, replacement); + if (replacement !== null) { + range.end = this.session.replace(range, replacement); + return range; + } else { + return null; + } + }; + Editor.prototype.getLastSearchOptions = function() { + return this.$search.getOptions(); + }; + Editor.prototype.find = function(needle, options, animate) { + if (!options) + options = {}; + if (typeof needle == "string" || needle instanceof RegExp) + options.needle = needle; + else if (typeof needle == "object") + oop.mixin(options, needle); + var range = this.selection.getRange(); + if (options.needle == null) { + needle = this.session.getTextRange(range) || + this.$search.$options.needle; + if (!needle) { + range = this.session.getWordRange(range.start.row, range.start.column); + needle = this.session.getTextRange(range); + } + this.$search.set({ + needle: needle + }); + } + this.$search.set(options); + if (!options.start) + this.$search.set({ + start: range + }); + var newRange = this.$search.find(this.session); + if (options.preventScroll) + return newRange; + if (newRange) { + this.revealRange(newRange, animate); + return newRange; + } + if (options.backwards) + range.start = range.end; + else + range.end = range.start; + this.selection.setRange(range); + }; + Editor.prototype.findNext = function(options, animate) { + this.find({ + skipCurrent: true, + backwards: false + }, options, animate); + }; + Editor.prototype.findPrevious = function(options, animate) { + this.find(options, { + skipCurrent: true, + backwards: true + }, animate); + }; + Editor.prototype.revealRange = function(range, animate) { + this.session.unfold(range); + this.selection.setSelectionRange(range); + var scrollTop = this.renderer.scrollTop; + this.renderer.scrollSelectionIntoView(range.start, range.end, 0.5); + if (animate !== false) + this.renderer.animateScrolling(scrollTop); + }; + Editor.prototype.undo = function() { + this.session.getUndoManager().undo(this.session); + this.renderer.scrollCursorIntoView(null, 0.5); + }; + Editor.prototype.redo = function() { + this.session.getUndoManager().redo(this.session); + this.renderer.scrollCursorIntoView(null, 0.5); + }; + Editor.prototype.destroy = function() { + if (this.$toDestroy) { + this.$toDestroy.forEach(function(el) { + el.destroy(); + }); + this.$toDestroy = null; + } + if (this.$mouseHandler) + this.$mouseHandler.destroy(); + this.renderer.destroy(); + this._signal("destroy", this); + if (this.session) + this.session.destroy(); + if (this._$emitInputEvent) + this._$emitInputEvent.cancel(); + this.removeAllListeners(); + }; + Editor.prototype.setAutoScrollEditorIntoView = function(enable) { + if (!enable) + return; + var rect; + var self = this; + var shouldScroll = false; + if (!this.$scrollAnchor) + this.$scrollAnchor = document.createElement("div"); + var scrollAnchor = this.$scrollAnchor; + scrollAnchor.style.cssText = "position:absolute"; + this.container.insertBefore(scrollAnchor, this.container.firstChild); + var onChangeSelection = this.on("changeSelection", function() { + shouldScroll = true; + }); + var onBeforeRender = this.renderer.on("beforeRender", function() { + if (shouldScroll) + rect = self.renderer.container.getBoundingClientRect(); + }); + var onAfterRender = this.renderer.on("afterRender", function() { + if (shouldScroll && rect && (self.isFocused() || + self.searchBox && self.searchBox.isFocused())) { + var renderer = self.renderer; + var pos = renderer.$cursorLayer.$pixelPos; + var config = renderer.layerConfig; + var top = pos.top - config.offset; + if (pos.top >= 0 && top + rect.top < 0) { + shouldScroll = true; + } else if (pos.top < config.height && + pos.top + rect.top + config.lineHeight > window.innerHeight) { + shouldScroll = false; + } else { + shouldScroll = null; + } + if (shouldScroll != null) { + scrollAnchor.style.top = top + "px"; + scrollAnchor.style.left = pos.left + "px"; + scrollAnchor.style.height = config.lineHeight + "px"; + scrollAnchor.scrollIntoView(shouldScroll); + } + shouldScroll = rect = null; + } + }); + this.setAutoScrollEditorIntoView = function(enable) { + if (enable) + return; + delete this.setAutoScrollEditorIntoView; + this.off("changeSelection", onChangeSelection); + this.renderer.off("afterRender", onAfterRender); + this.renderer.off("beforeRender", onBeforeRender); + }; + }; + Editor.prototype.$resetCursorStyle = function() { + var style = this.$cursorStyle || "ace"; + var cursorLayer = this.renderer.$cursorLayer; + if (!cursorLayer) + return; + cursorLayer.setSmoothBlinking(/smooth/.test(style)); + cursorLayer.isBlinking = !this.$readOnly && style != "wide"; + dom.setCssClass(cursorLayer.element, "ace_slim-cursors", /slim/.test(style)); + }; + Editor.prototype.prompt = function(message, options, callback) { + var editor = this; + config.loadModule("ace/ext/prompt", function(module) { + module.prompt(editor, message, options, callback); + }); + }; + return Editor; + }()); + Editor.$uid = 0; + Editor.prototype.curOp = null; + Editor.prototype.prevOp = {}; + Editor.prototype.$mergeableCommands = ["backspace", "del", "insertstring"]; + Editor.prototype.$toggleWordPairs = [ + ["first", "last"], + ["true", "false"], + ["yes", "no"], + ["width", "height"], + ["top", "bottom"], + ["right", "left"], + ["on", "off"], + ["x", "y"], + ["get", "set"], + ["max", "min"], + ["horizontal", "vertical"], + ["show", "hide"], + ["add", "remove"], + ["up", "down"], + ["before", "after"], + ["even", "odd"], + ["in", "out"], + ["inside", "outside"], + ["next", "previous"], + ["increase", "decrease"], + ["attach", "detach"], + ["&&", "||"], + ["==", "!="] + ]; + oop.implement(Editor.prototype, EventEmitter); + config.defineOptions(Editor.prototype, "editor", { + selectionStyle: { + set: function(style) { + this.onSelectionChange(); + this._signal("changeSelectionStyle", { + data: style + }); + }, + initialValue: "line" + }, + highlightActiveLine: { + set: function() { + this.$updateHighlightActiveLine(); + }, + initialValue: true + }, + highlightSelectedWord: { + set: function(shouldHighlight) { + this.$onSelectionChange(); + }, + initialValue: true + }, + readOnly: { + set: function(readOnly) { + this.textInput.setReadOnly(readOnly); + this.$resetCursorStyle(); + }, + initialValue: false + }, + copyWithEmptySelection: { + set: function(value) { + this.textInput.setCopyWithEmptySelection(value); + }, + initialValue: false + }, + cursorStyle: { + set: function(val) { + this.$resetCursorStyle(); + }, + values: ["ace", "slim", "smooth", "wide"], + initialValue: "ace" + }, + mergeUndoDeltas: { + values: [false, true, "always"], + initialValue: true + }, + behavioursEnabled: { + initialValue: true + }, + wrapBehavioursEnabled: { + initialValue: true + }, + enableAutoIndent: { + initialValue: true + }, + autoScrollEditorIntoView: { + set: function(val) { + this.setAutoScrollEditorIntoView(val); + } + }, + keyboardHandler: { + set: function(val) { + this.setKeyboardHandler(val); + }, + get: function() { + return this.$keybindingId; + }, + handlesSet: true + }, + value: { + set: function(val) { + this.session.setValue(val); + }, + get: function() { + return this.getValue(); + }, + handlesSet: true, + hidden: true + }, + session: { + set: function(val) { + this.setSession(val); + }, + get: function() { + return this.session; + }, + handlesSet: true, + hidden: true + }, + showLineNumbers: { + set: function(show) { + this.renderer.$gutterLayer.setShowLineNumbers(show); + this.renderer.$loop.schedule(this.renderer.CHANGE_GUTTER); + if (show && this.$relativeLineNumbers) + relativeNumberRenderer.attach(this); + else + relativeNumberRenderer.detach(this); + }, + initialValue: true + }, + relativeLineNumbers: { + set: function(value) { + if (this.$showLineNumbers && value) + relativeNumberRenderer.attach(this); + else + relativeNumberRenderer.detach(this); + } + }, + placeholder: { + set: function(message) { + if (!this.$updatePlaceholder) { + this.$updatePlaceholder = function() { + var hasValue = this.session && (this.renderer.$composition || + this.session.getLength() > 1 || this.session.getLine(0).length > 0); + if (hasValue && this.renderer.placeholderNode) { + this.renderer.off("afterRender", this.$updatePlaceholder); + dom.removeCssClass(this.container, "ace_hasPlaceholder"); + this.renderer.placeholderNode.remove(); + this.renderer.placeholderNode = null; + } else if (!hasValue && !this.renderer.placeholderNode) { + this.renderer.on("afterRender", this.$updatePlaceholder); + dom.addCssClass(this.container, "ace_hasPlaceholder"); + var el = dom.createElement("div"); + el.className = "ace_placeholder"; + el.textContent = this.$placeholder || ""; + this.renderer.placeholderNode = el; + this.renderer.content.appendChild(this.renderer.placeholderNode); + } else if (!hasValue && this.renderer.placeholderNode) { + this.renderer.placeholderNode.textContent = this.$placeholder || ""; + } + }.bind(this); + this.on("input", this.$updatePlaceholder); + } + this.$updatePlaceholder(); + } + }, + enableKeyboardAccessibility: { + set: function(value) { + var blurCommand = { + name: "blurTextInput", + description: "Set focus to the editor content div to allow tabbing through the page", + bindKey: "Esc", + exec: function(editor) { + editor.blur(); + editor.renderer.scroller.focus(); + }, + readOnly: true + }; + var focusOnEnterKeyup = function(e) { + if (e.target == this.renderer.scroller && e.keyCode === keys['enter']) { + e.preventDefault(); + var row = this.getCursorPosition().row; + if (!this.isRowVisible(row)) + this.scrollToLine(row, true, true); + this.focus(); + } + }; + var gutterKeyboardHandler; + if (value) { + this.renderer.enableKeyboardAccessibility = true; + this.renderer.keyboardFocusClassName = "ace_keyboard-focus"; + this.textInput.getElement().setAttribute("tabindex", -1); + this.textInput.setNumberOfExtraLines(useragent.isWin ? 3 : 0); + this.renderer.scroller.setAttribute("tabindex", 0); + this.renderer.scroller.setAttribute("role", "group"); + this.renderer.scroller.setAttribute("aria-roledescription", nls("editor.scroller.aria-roledescription", "editor")); + this.renderer.scroller.classList.add(this.renderer.keyboardFocusClassName); + this.renderer.scroller.setAttribute("aria-label", nls("editor.scroller.aria-label", "Editor content, press Enter to start editing, press Escape to exit")); + this.renderer.scroller.addEventListener("keyup", focusOnEnterKeyup.bind(this)); + this.commands.addCommand(blurCommand); + this.renderer.$gutter.setAttribute("tabindex", 0); + this.renderer.$gutter.setAttribute("aria-hidden", false); + this.renderer.$gutter.setAttribute("role", "group"); + this.renderer.$gutter.setAttribute("aria-roledescription", nls("editor.gutter.aria-roledescription", "editor")); + this.renderer.$gutter.setAttribute("aria-label", nls("editor.gutter.aria-label", "Editor gutter, press Enter to interact with controls using arrow keys, press Escape to exit")); + this.renderer.$gutter.classList.add(this.renderer.keyboardFocusClassName); + this.renderer.content.setAttribute("aria-hidden", true); + if (!gutterKeyboardHandler) + gutterKeyboardHandler = new GutterKeyboardHandler(this); + gutterKeyboardHandler.addListener(); + this.textInput.setAriaOptions({ + setLabel: true + }); + } else { + this.renderer.enableKeyboardAccessibility = false; + this.textInput.getElement().setAttribute("tabindex", 0); + this.textInput.setNumberOfExtraLines(0); + this.renderer.scroller.setAttribute("tabindex", -1); + this.renderer.scroller.removeAttribute("role"); + this.renderer.scroller.removeAttribute("aria-roledescription"); + this.renderer.scroller.classList.remove(this.renderer.keyboardFocusClassName); + this.renderer.scroller.removeAttribute("aria-label"); + this.renderer.scroller.removeEventListener("keyup", focusOnEnterKeyup.bind(this)); + this.commands.removeCommand(blurCommand); + this.renderer.content.removeAttribute("aria-hidden"); + this.renderer.$gutter.setAttribute("tabindex", -1); + this.renderer.$gutter.setAttribute("aria-hidden", true); + this.renderer.$gutter.removeAttribute("role"); + this.renderer.$gutter.removeAttribute("aria-roledescription"); + this.renderer.$gutter.removeAttribute("aria-label"); + this.renderer.$gutter.classList.remove(this.renderer.keyboardFocusClassName); + if (gutterKeyboardHandler) + gutterKeyboardHandler.removeListener(); + } + }, + initialValue: false + }, + textInputAriaLabel: { + set: function(val) { + this.$textInputAriaLabel = val; + }, + initialValue: "" + }, + enableMobileMenu: { + set: function(val) { + this.$enableMobileMenu = val; + }, + initialValue: true + }, + customScrollbar: "renderer", + hScrollBarAlwaysVisible: "renderer", + vScrollBarAlwaysVisible: "renderer", + highlightGutterLine: "renderer", + animatedScroll: "renderer", + showInvisibles: "renderer", + showPrintMargin: "renderer", + printMarginColumn: "renderer", + printMargin: "renderer", + fadeFoldWidgets: "renderer", + showFoldWidgets: "renderer", + displayIndentGuides: "renderer", + highlightIndentGuides: "renderer", + showGutter: "renderer", + fontSize: "renderer", + fontFamily: "renderer", + maxLines: "renderer", + minLines: "renderer", + scrollPastEnd: "renderer", + fixedWidthGutter: "renderer", + theme: "renderer", + hasCssTransforms: "renderer", + maxPixelHeight: "renderer", + useTextareaForIME: "renderer", + useResizeObserver: "renderer", + useSvgGutterIcons: "renderer", + showFoldedAnnotations: "renderer", + scrollSpeed: "$mouseHandler", + dragDelay: "$mouseHandler", + dragEnabled: "$mouseHandler", + focusTimeout: "$mouseHandler", + tooltipFollowsMouse: "$mouseHandler", + firstLineNumber: "session", + overwrite: "session", + newLineMode: "session", + useWorker: "session", + useSoftTabs: "session", + navigateWithinSoftTabs: "session", + tabSize: "session", + wrap: "session", + indentedSoftWrap: "session", + foldStyle: "session", + mode: "session" + }); + var relativeNumberRenderer = { + getText: function( /**@type{EditSession}*/ session, /**@type{number}*/ row) { + return (Math.abs(session.selection.lead.row - row) || (row + 1 + (row < 9 ? "\xb7" : ""))) + ""; + }, + getWidth: function(session, /**@type{number}*/ lastLineNumber, config) { + return Math.max(lastLineNumber.toString().length, (config.lastRow + 1).toString().length, 2) * config.characterWidth; + }, + update: function(e, /**@type{Editor}*/ editor) { + editor.renderer.$loop.schedule(editor.renderer.CHANGE_GUTTER); + }, + attach: function( /**@type{Editor}*/ editor) { + editor.renderer.$gutterLayer.$renderer = this; + editor.on("changeSelection", this.update); + this.update(null, editor); + }, + detach: function( /**@type{Editor}*/ editor) { + if (editor.renderer.$gutterLayer.$renderer == this) + editor.renderer.$gutterLayer.$renderer = null; + editor.off("changeSelection", this.update); + this.update(null, editor); + } + }; + exports.Editor = Editor; + +}); + +define("ace/layer/lines", ["require", "exports", "module", "ace/lib/dom"], function(require, exports, module) { + "use strict"; + var dom = require("../lib/dom"); + var Lines = /** @class */ (function() { + function Lines(element, canvasHeight) { + this.element = element; + this.canvasHeight = canvasHeight || 500000; + this.element.style.height = (this.canvasHeight * 2) + "px"; + this.cells = []; + this.cellCache = []; + this.$offsetCoefficient = 0; + } + Lines.prototype.moveContainer = function(config) { + dom.translate(this.element, 0, -((config.firstRowScreen * config.lineHeight) % this.canvasHeight) - config.offset * this.$offsetCoefficient); + }; + Lines.prototype.pageChanged = function(oldConfig, newConfig) { + return (Math.floor((oldConfig.firstRowScreen * oldConfig.lineHeight) / this.canvasHeight) !== + Math.floor((newConfig.firstRowScreen * newConfig.lineHeight) / this.canvasHeight)); + }; + Lines.prototype.computeLineTop = function(row, config, session) { + var screenTop = config.firstRowScreen * config.lineHeight; + var screenPage = Math.floor(screenTop / this.canvasHeight); + var lineTop = session.documentToScreenRow(row, 0) * config.lineHeight; + return lineTop - (screenPage * this.canvasHeight); + }; + Lines.prototype.computeLineHeight = function(row, config, session) { + return config.lineHeight * session.getRowLineCount(row); + }; + Lines.prototype.getLength = function() { + return this.cells.length; + }; + Lines.prototype.get = function(index) { + return this.cells[index]; + }; + Lines.prototype.shift = function() { + this.$cacheCell(this.cells.shift()); + }; + Lines.prototype.pop = function() { + this.$cacheCell(this.cells.pop()); + }; + Lines.prototype.push = function(cell) { + if (Array.isArray(cell)) { + this.cells.push.apply(this.cells, cell); + var fragment = dom.createFragment(this.element); + for (var i = 0; i < cell.length; i++) { + fragment.appendChild(cell[i].element); + } + this.element.appendChild(fragment); + } else { + this.cells.push(cell); + this.element.appendChild(cell.element); + } + }; + Lines.prototype.unshift = function(cell) { + if (Array.isArray(cell)) { + this.cells.unshift.apply(this.cells, cell); + var fragment = dom.createFragment(this.element); + for (var i = 0; i < cell.length; i++) { + fragment.appendChild(cell[i].element); + } + if (this.element.firstChild) + this.element.insertBefore(fragment, this.element.firstChild); + else + this.element.appendChild(fragment); + } else { + this.cells.unshift(cell); + this.element.insertAdjacentElement("afterbegin", cell.element); + } + }; + Lines.prototype.last = function() { + if (this.cells.length) + return this.cells[this.cells.length - 1]; + else + return null; + }; + Lines.prototype.$cacheCell = function(cell) { + if (!cell) + return; + cell.element.remove(); + this.cellCache.push(cell); + }; + Lines.prototype.createCell = function(row, config, session, initElement) { + var cell = this.cellCache.pop(); + if (!cell) { + var element = dom.createElement("div"); + if (initElement) + initElement(element); + this.element.appendChild(element); + cell = { + element: element, + text: "", + row: row + }; + } + cell.row = row; + return cell; + }; + return Lines; + }()); + exports.Lines = Lines; + +}); + +define("ace/layer/gutter", ["require", "exports", "module", "ace/lib/dom", "ace/lib/oop", "ace/lib/lang", "ace/lib/event_emitter", "ace/layer/lines", "ace/config"], function(require, exports, module) { + "use strict"; + var dom = require("../lib/dom"); + var oop = require("../lib/oop"); + var lang = require("../lib/lang"); + var EventEmitter = require("../lib/event_emitter").EventEmitter; + var Lines = require("./lines").Lines; + var nls = require("../config").nls; + var Gutter = /** @class */ (function() { + function Gutter(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_gutter-layer"; + parentEl.appendChild(this.element); + this.setShowFoldWidgets(this.$showFoldWidgets); + this.gutterWidth = 0; + this.$annotations = []; + this.$updateAnnotations = this.$updateAnnotations.bind(this); + this.$lines = new Lines(this.element); + this.$lines.$offsetCoefficient = 1; + } + Gutter.prototype.setSession = function(session) { + if (this.session) + this.session.off("change", this.$updateAnnotations); + this.session = session; + if (session) + session.on("change", this.$updateAnnotations); + }; + Gutter.prototype.addGutterDecoration = function(row, className) { + if (window.console) + console.warn && console.warn("deprecated use session.addGutterDecoration"); + this.session.addGutterDecoration(row, className); + }; + Gutter.prototype.removeGutterDecoration = function(row, className) { + if (window.console) + console.warn && console.warn("deprecated use session.removeGutterDecoration"); + this.session.removeGutterDecoration(row, className); + }; + Gutter.prototype.setAnnotations = function(annotations) { + this.$annotations = []; + for (var i = 0; i < annotations.length; i++) { + var annotation = annotations[i]; + var row = annotation.row; + var rowInfo = this.$annotations[row]; + if (!rowInfo) + rowInfo = this.$annotations[row] = { + text: [], + type: [], + displayText: [] + }; + var annoText = annotation.text; + var displayAnnoText = annotation.text; + var annoType = annotation.type; + annoText = annoText ? lang.escapeHTML(annoText) : annotation.html || ""; + displayAnnoText = displayAnnoText ? displayAnnoText : annotation.html || ""; + if (rowInfo.text.indexOf(annoText) === -1) { + rowInfo.text.push(annoText); + rowInfo.type.push(annoType); + rowInfo.displayText.push(displayAnnoText); + } + var className = annotation.className; + if (className) + rowInfo.className = className; + else if (annoType == "error") + rowInfo.className = " ace_error"; + else if (annoType == "warning" && rowInfo.className != " ace_error") + rowInfo.className = " ace_warning"; + else if (annoType == "info" && (!rowInfo.className)) + rowInfo.className = " ace_info"; + } + }; + Gutter.prototype.$updateAnnotations = function(delta) { + if (!this.$annotations.length) + return; + var firstRow = delta.start.row; + var len = delta.end.row - firstRow; + if (len === 0) {} else if (delta.action == 'remove') { + this.$annotations.splice(firstRow, len + 1, null); + } else { + var args = new Array(len + 1); + args.unshift(firstRow, 1); + this.$annotations.splice.apply(this.$annotations, args); + } + }; + Gutter.prototype.update = function(config) { + this.config = config; + var session = this.session; + var firstRow = config.firstRow; + var lastRow = Math.min(config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar + session.getLength() - 1); + this.oldLastRow = lastRow; + this.config = config; + this.$lines.moveContainer(config); + this.$updateCursorRow(); + var fold = session.getNextFoldLine(firstRow); + var foldStart = fold ? fold.start.row : Infinity; + var cell = null; + var index = -1; + var row = firstRow; + while (true) { + if (row > foldStart) { + row = fold.end.row + 1; + fold = session.getNextFoldLine(row, fold); + foldStart = fold ? fold.start.row : Infinity; + } + if (row > lastRow) { + while (this.$lines.getLength() > index + 1) + this.$lines.pop(); + break; + } + cell = this.$lines.get(++index); + if (cell) { + cell.row = row; + } else { + cell = this.$lines.createCell(row, config, this.session, onCreateCell); + this.$lines.push(cell); + } + this.$renderCell(cell, config, fold, row); + row++; + } + this._signal("afterRender"); + this.$updateGutterWidth(config); + }; + Gutter.prototype.$updateGutterWidth = function(config) { + var session = this.session; + var gutterRenderer = session.gutterRenderer || this.$renderer; + var firstLineNumber = session.$firstLineNumber; + var lastLineText = this.$lines.last() ? this.$lines.last().text : ""; + if (this.$fixedWidth || session.$useWrapMode) + lastLineText = session.getLength() + firstLineNumber - 1; + var gutterWidth = gutterRenderer ? + gutterRenderer.getWidth(session, lastLineText, config) : + lastLineText.toString().length * config.characterWidth; + var padding = this.$padding || this.$computePadding(); + gutterWidth += padding.left + padding.right; + if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) { + this.gutterWidth = gutterWidth; + (this.element.parentNode).style.width = + this.element.style.width = Math.ceil(this.gutterWidth) + "px"; + this._signal("changeGutterWidth", gutterWidth); + } + }; + Gutter.prototype.$updateCursorRow = function() { + if (!this.$highlightGutterLine) + return; + var position = this.session.selection.getCursor(); + if (this.$cursorRow === position.row) + return; + this.$cursorRow = position.row; + }; + Gutter.prototype.updateLineHighlight = function() { + if (!this.$highlightGutterLine) + return; + var row = this.session.selection.cursor.row; + this.$cursorRow = row; + if (this.$cursorCell && this.$cursorCell.row == row) + return; + if (this.$cursorCell) + this.$cursorCell.element.className = this.$cursorCell.element.className.replace("ace_gutter-active-line ", ""); + var cells = this.$lines.cells; + this.$cursorCell = null; + for (var i = 0; i < cells.length; i++) { + var cell = cells[i]; + if (cell.row >= this.$cursorRow) { + if (cell.row > this.$cursorRow) { + var fold = this.session.getFoldLine(this.$cursorRow); + if (i > 0 && fold && fold.start.row == cells[i - 1].row) + cell = cells[i - 1]; + else + break; + } + cell.element.className = "ace_gutter-active-line " + cell.element.className; + this.$cursorCell = cell; + break; + } + } + }; + Gutter.prototype.scrollLines = function(config) { + var oldConfig = this.config; + this.config = config; + this.$updateCursorRow(); + if (this.$lines.pageChanged(oldConfig, config)) + return this.update(config); + this.$lines.moveContainer(config); + var lastRow = Math.min(config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar + this.session.getLength() - 1); + var oldLastRow = this.oldLastRow; + this.oldLastRow = lastRow; + if (!oldConfig || oldLastRow < config.firstRow) + return this.update(config); + if (lastRow < oldConfig.firstRow) + return this.update(config); + if (oldConfig.firstRow < config.firstRow) + for (var row = this.session.getFoldedRowCount(oldConfig.firstRow, config.firstRow - 1); row > 0; row--) + this.$lines.shift(); + if (oldLastRow > lastRow) + for (var row = this.session.getFoldedRowCount(lastRow + 1, oldLastRow); row > 0; row--) + this.$lines.pop(); + if (config.firstRow < oldConfig.firstRow) { + this.$lines.unshift(this.$renderLines(config, config.firstRow, oldConfig.firstRow - 1)); + } + if (lastRow > oldLastRow) { + this.$lines.push(this.$renderLines(config, oldLastRow + 1, lastRow)); + } + this.updateLineHighlight(); + this._signal("afterRender"); + this.$updateGutterWidth(config); + }; + Gutter.prototype.$renderLines = function(config, firstRow, lastRow) { + var fragment = []; + var row = firstRow; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + while (true) { + if (row > foldStart) { + row = foldLine.end.row + 1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + if (row > lastRow) + break; + var cell = this.$lines.createCell(row, config, this.session, onCreateCell); + this.$renderCell(cell, config, foldLine, row); + fragment.push(cell); + row++; + } + return fragment; + }; + Gutter.prototype.$renderCell = function(cell, config, fold, row) { + var element = cell.element; + var session = this.session; + var textNode = element.childNodes[0]; + var foldWidget = element.childNodes[1]; + var annotationNode = element.childNodes[2]; + var annotationIconNode = annotationNode.firstChild; + var firstLineNumber = session.$firstLineNumber; + var breakpoints = session.$breakpoints; + var decorations = session.$decorations; + var gutterRenderer = session.gutterRenderer || this.$renderer; + var foldWidgets = this.$showFoldWidgets && session.foldWidgets; + var foldStart = fold ? fold.start.row : Number.MAX_VALUE; + var lineHeight = config.lineHeight + "px"; + var className = this.$useSvgGutterIcons ? "ace_gutter-cell_svg-icons " : "ace_gutter-cell "; + var iconClassName = this.$useSvgGutterIcons ? "ace_icon_svg" : "ace_icon"; + var rowText = (gutterRenderer ? + gutterRenderer.getText(session, row) : + row + firstLineNumber).toString(); + if (this.$highlightGutterLine) { + if (row == this.$cursorRow || (fold && row < this.$cursorRow && row >= foldStart && this.$cursorRow <= fold.end.row)) { + className += "ace_gutter-active-line "; + if (this.$cursorCell != cell) { + if (this.$cursorCell) + this.$cursorCell.element.className = this.$cursorCell.element.className.replace("ace_gutter-active-line ", ""); + this.$cursorCell = cell; + } + } + } + if (breakpoints[row]) + className += breakpoints[row]; + if (decorations[row]) + className += decorations[row]; + if (this.$annotations[row] && row !== foldStart) + className += this.$annotations[row].className; + if (foldWidgets) { + var c = foldWidgets[row]; + if (c == null) + c = foldWidgets[row] = session.getFoldWidget(row); + } + if (c) { + var foldClass = "ace_fold-widget ace_" + c; + var isClosedFold = c == "start" && row == foldStart && row < fold.end.row; + if (isClosedFold) { + foldClass += " ace_closed"; + var foldAnnotationClass = ''; + var annotationInFold = false; + for (var i = row + 1; i <= fold.end.row; i++) { + if (!this.$annotations[i]) + continue; + if (this.$annotations[i].className === " ace_error") { + annotationInFold = true; + foldAnnotationClass = " ace_error_fold"; + break; + } + if (this.$annotations[i].className === " ace_warning") { + annotationInFold = true; + foldAnnotationClass = " ace_warning_fold"; + continue; + } + } + className += foldAnnotationClass; + } else + foldClass += " ace_open"; + if (foldWidget.className != foldClass) + foldWidget.className = foldClass; + dom.setStyle(foldWidget.style, "height", lineHeight); + dom.setStyle(foldWidget.style, "display", "inline-block"); + foldWidget.setAttribute("role", "button"); + foldWidget.setAttribute("tabindex", "-1"); + var foldRange = session.getFoldWidgetRange(row); + if (foldRange) + foldWidget.setAttribute("aria-label", nls("gutter.code-folding.range.aria-label", "Toggle code folding, rows $0 through $1", [foldRange.start.row + 1, foldRange.end.row + 1])); + else { + if (fold) + foldWidget.setAttribute("aria-label", nls("gutter.code-folding.closed.aria-label", "Toggle code folding, rows $0 through $1", [fold.start.row + 1, fold.end.row + 1])); + else + foldWidget.setAttribute("aria-label", nls("gutter.code-folding.open.aria-label", "Toggle code folding, row $0", [row + 1])); + } + if (isClosedFold) { + foldWidget.setAttribute("aria-expanded", "false"); + foldWidget.setAttribute("title", nls("gutter.code-folding.closed.title", "Unfold code")); + } else { + foldWidget.setAttribute("aria-expanded", "true"); + foldWidget.setAttribute("title", nls("gutter.code-folding.open.title", "Fold code")); + } + } else { + if (foldWidget) { + dom.setStyle(foldWidget.style, "display", "none"); + foldWidget.setAttribute("tabindex", "0"); + foldWidget.removeAttribute("role"); + foldWidget.removeAttribute("aria-label"); + } + } + if (annotationInFold && this.$showFoldedAnnotations) { + annotationNode.className = "ace_gutter_annotation"; + annotationIconNode.className = iconClassName; + annotationIconNode.className += foldAnnotationClass; + dom.setStyle(annotationIconNode.style, "height", lineHeight); + dom.setStyle(annotationNode.style, "display", "block"); + dom.setStyle(annotationNode.style, "height", lineHeight); + var ariaLabel; + switch (foldAnnotationClass) { + case " ace_error_fold": + ariaLabel = nls("gutter.annotation.aria-label.error", "Read annotations row $0", [rowText]); + break; + case " ace_warning_fold": + ariaLabel = nls("gutter.annotation.aria-label.warning", "Read annotations row $0", [rowText]); + break; + } + annotationNode.setAttribute("aria-label", ariaLabel); + annotationNode.setAttribute("tabindex", "-1"); + annotationNode.setAttribute("role", "button"); + } else if (this.$annotations[row]) { + annotationNode.className = "ace_gutter_annotation"; + annotationIconNode.className = iconClassName; + if (this.$useSvgGutterIcons) + annotationIconNode.className += this.$annotations[row].className; + else + element.classList.add(this.$annotations[row].className.replace(" ", "")); + dom.setStyle(annotationIconNode.style, "height", lineHeight); + dom.setStyle(annotationNode.style, "display", "block"); + dom.setStyle(annotationNode.style, "height", lineHeight); + var ariaLabel; + switch (this.$annotations[row].className) { + case " ace_error": + ariaLabel = nls("gutter.annotation.aria-label.error", "Read annotations row $0", [rowText]); + break; + case " ace_warning": + ariaLabel = nls("gutter.annotation.aria-label.warning", "Read annotations row $0", [rowText]); + break; + case " ace_info": + ariaLabel = nls("gutter.annotation.aria-label.info", "Read annotations row $0", [rowText]); + break; + } + annotationNode.setAttribute("aria-label", ariaLabel); + annotationNode.setAttribute("tabindex", "-1"); + annotationNode.setAttribute("role", "button"); + } else { + dom.setStyle(annotationNode.style, "display", "none"); + annotationNode.removeAttribute("aria-label"); + annotationNode.removeAttribute("role"); + annotationNode.setAttribute("tabindex", "0"); + } + if (rowText !== textNode.data) { + textNode.data = rowText; + } + if (element.className != className) + element.className = className; + dom.setStyle(cell.element.style, "height", this.$lines.computeLineHeight(row, config, session) + "px"); + dom.setStyle(cell.element.style, "top", this.$lines.computeLineTop(row, config, session) + "px"); + cell.text = rowText; + if (annotationNode.style.display === "none" && foldWidget.style.display === "none") + cell.element.setAttribute("aria-hidden", true); + else + cell.element.setAttribute("aria-hidden", false); + return cell; + }; + Gutter.prototype.setHighlightGutterLine = function(highlightGutterLine) { + this.$highlightGutterLine = highlightGutterLine; + }; + Gutter.prototype.setShowLineNumbers = function(show) { + this.$renderer = !show && { + getWidth: function() { + return 0; + }, + getText: function() { + return ""; + } + }; + }; + Gutter.prototype.getShowLineNumbers = function() { + return this.$showLineNumbers; + }; + Gutter.prototype.setShowFoldWidgets = function(show) { + if (show) + dom.addCssClass(this.element, "ace_folding-enabled"); + else + dom.removeCssClass(this.element, "ace_folding-enabled"); + this.$showFoldWidgets = show; + this.$padding = null; + }; + Gutter.prototype.getShowFoldWidgets = function() { + return this.$showFoldWidgets; + }; + Gutter.prototype.$computePadding = function() { + if (!this.element.firstChild) + return { + left: 0, + right: 0 + }; + var style = dom.computedStyle( /**@type{Element}*/ (this.element.firstChild)); + this.$padding = {}; + this.$padding.left = (parseInt(style.borderLeftWidth) || 0) + + (parseInt(style.paddingLeft) || 0) + 1; + this.$padding.right = (parseInt(style.borderRightWidth) || 0) + + (parseInt(style.paddingRight) || 0); + return this.$padding; + }; + Gutter.prototype.getRegion = function(point) { + var padding = this.$padding || this.$computePadding(); + var rect = this.element.getBoundingClientRect(); + if (point.x < padding.left + rect.left) + return "markers"; + if (this.$showFoldWidgets && point.x > rect.right - padding.right) + return "foldWidgets"; + }; + return Gutter; + }()); + Gutter.prototype.$fixedWidth = false; + Gutter.prototype.$highlightGutterLine = true; + Gutter.prototype.$renderer = ""; + Gutter.prototype.$showLineNumbers = true; + Gutter.prototype.$showFoldWidgets = true; + oop.implement(Gutter.prototype, EventEmitter); + + function onCreateCell(element) { + var textNode = document.createTextNode(''); + element.appendChild(textNode); + var foldWidget = dom.createElement("span"); + element.appendChild(foldWidget); + var annotationNode = dom.createElement("span"); + element.appendChild(annotationNode); + var annotationIconNode = dom.createElement("span"); + annotationNode.appendChild(annotationIconNode); + return element; + } + exports.Gutter = Gutter; + +}); + +define("ace/layer/marker", ["require", "exports", "module", "ace/range", "ace/lib/dom"], function(require, exports, module) { + "use strict"; + var Range = require("../range").Range; + var dom = require("../lib/dom"); + var Marker = /** @class */ (function() { + function Marker(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_marker-layer"; + parentEl.appendChild(this.element); + } + Marker.prototype.setPadding = function(padding) { + this.$padding = padding; + }; + Marker.prototype.setSession = function(session) { + this.session = session; + }; + Marker.prototype.setMarkers = function(markers) { + this.markers = markers; + }; + Marker.prototype.elt = function(className, css) { + var x = this.i != -1 && this.element.childNodes[this.i]; + if (!x) { + x = document.createElement("div"); + this.element.appendChild(x); + this.i = -1; + } else { + this.i++; + } + x.style.cssText = css; + x.className = className; + }; + Marker.prototype.update = function(config) { + if (!config) + return; + this.config = config; + this.i = 0; + var html; + for (var key in this.markers) { + var marker = this.markers[key]; + if (!marker.range) { + marker.update(html, this, this.session, config); + continue; + } + var range = marker.range.clipRows(config.firstRow, config.lastRow); + if (range.isEmpty()) + continue; + range = range.toScreenRange(this.session); + if (marker.renderer) { + var top = this.$getTop(range.start.row, config); + var left = this.$padding + range.start.column * config.characterWidth; + marker.renderer(html, range, left, top, config); + } else if (marker.type == "fullLine") { + this.drawFullLineMarker(html, range, marker.clazz, config); + } else if (marker.type == "screenLine") { + this.drawScreenLineMarker(html, range, marker.clazz, config); + } else if (range.isMultiLine()) { + if (marker.type == "text") + this.drawTextMarker(html, range, marker.clazz, config); + else + this.drawMultiLineMarker(html, range, marker.clazz, config); + } else { + this.drawSingleLineMarker(html, range, marker.clazz + " ace_start" + " ace_br15", config); + } + } + if (this.i != -1) { + while (this.i < this.element.childElementCount) + this.element.removeChild(this.element.lastChild); + } + }; + Marker.prototype.$getTop = function(row, layerConfig) { + return (row - layerConfig.firstRowScreen) * layerConfig.lineHeight; + }; + Marker.prototype.drawTextMarker = function(stringBuilder, range, clazz, layerConfig, extraStyle) { + var session = this.session; + var start = range.start.row; + var end = range.end.row; + var row = start; + var prev = 0; + var curr = 0; + var next = session.getScreenLastRowColumn(row); + var lineRange = new Range(row, range.start.column, row, curr); + for (; row <= end; row++) { + lineRange.start.row = lineRange.end.row = row; + lineRange.start.column = row == start ? range.start.column : session.getRowWrapIndent(row); + lineRange.end.column = next; + prev = curr; + curr = next; + next = row + 1 < end ? session.getScreenLastRowColumn(row + 1) : row == end ? 0 : range.end.column; + this.drawSingleLineMarker(stringBuilder, lineRange, clazz + (row == start ? " ace_start" : "") + " ace_br" + + getBorderClass(row == start || row == start + 1 && range.start.column, prev < curr, curr > next, row == end), layerConfig, row == end ? 0 : 1, extraStyle); + } + }; + Marker.prototype.drawMultiLineMarker = function(stringBuilder, range, clazz, config, extraStyle) { + var padding = this.$padding; + var height = config.lineHeight; + var top = this.$getTop(range.start.row, config); + var left = padding + range.start.column * config.characterWidth; + extraStyle = extraStyle || ""; + if (this.session.$bidiHandler.isBidiRow(range.start.row)) { + var range1 = range.clone(); + range1.end.row = range1.start.row; + range1.end.column = this.session.getLine(range1.start.row).length; + this.drawBidiSingleLineMarker(stringBuilder, range1, clazz + " ace_br1 ace_start", config, null, extraStyle); + } else { + this.elt(clazz + " ace_br1 ace_start", "height:" + height + "px;" + "right:0;" + "top:" + top + "px;left:" + left + "px;" + (extraStyle || "")); + } + if (this.session.$bidiHandler.isBidiRow(range.end.row)) { + var range1 = range.clone(); + range1.start.row = range1.end.row; + range1.start.column = 0; + this.drawBidiSingleLineMarker(stringBuilder, range1, clazz + " ace_br12", config, null, extraStyle); + } else { + top = this.$getTop(range.end.row, config); + var width = range.end.column * config.characterWidth; + this.elt(clazz + " ace_br12", "height:" + height + "px;" + + "width:" + width + "px;" + + "top:" + top + "px;" + + "left:" + padding + "px;" + (extraStyle || "")); + } + height = (range.end.row - range.start.row - 1) * config.lineHeight; + if (height <= 0) + return; + top = this.$getTop(range.start.row + 1, config); + var radiusClass = (range.start.column ? 1 : 0) | (range.end.column ? 0 : 8); + this.elt(clazz + (radiusClass ? " ace_br" + radiusClass : ""), "height:" + height + "px;" + + "right:0;" + + "top:" + top + "px;" + + "left:" + padding + "px;" + (extraStyle || "")); + }; + Marker.prototype.drawSingleLineMarker = function(stringBuilder, range, clazz, config, extraLength, extraStyle) { + if (this.session.$bidiHandler.isBidiRow(range.start.row)) + return this.drawBidiSingleLineMarker(stringBuilder, range, clazz, config, extraLength, extraStyle); + var height = config.lineHeight; + var width = (range.end.column + (extraLength || 0) - range.start.column) * config.characterWidth; + var top = this.$getTop(range.start.row, config); + var left = this.$padding + range.start.column * config.characterWidth; + this.elt(clazz, "height:" + height + "px;" + + "width:" + width + "px;" + + "top:" + top + "px;" + + "left:" + left + "px;" + (extraStyle || "")); + }; + Marker.prototype.drawBidiSingleLineMarker = function(stringBuilder, range, clazz, config, extraLength, extraStyle) { + var height = config.lineHeight, + top = this.$getTop(range.start.row, config), + padding = this.$padding; + var selections = this.session.$bidiHandler.getSelections(range.start.column, range.end.column); + selections.forEach(function(selection) { + this.elt(clazz, "height:" + height + "px;" + + "width:" + (selection.width + (extraLength || 0)) + "px;" + + "top:" + top + "px;" + + "left:" + (padding + selection.left) + "px;" + (extraStyle || "")); + }, this); + }; + Marker.prototype.drawFullLineMarker = function(stringBuilder, range, clazz, config, extraStyle) { + var top = this.$getTop(range.start.row, config); + var height = config.lineHeight; + if (range.start.row != range.end.row) + height += this.$getTop(range.end.row, config) - top; + this.elt(clazz, "height:" + height + "px;" + + "top:" + top + "px;" + + "left:0;right:0;" + (extraStyle || "")); + }; + Marker.prototype.drawScreenLineMarker = function(stringBuilder, range, clazz, config, extraStyle) { + var top = this.$getTop(range.start.row, config); + var height = config.lineHeight; + this.elt(clazz, "height:" + height + "px;" + + "top:" + top + "px;" + + "left:0;right:0;" + (extraStyle || "")); + }; + return Marker; + }()); + Marker.prototype.$padding = 0; + + function getBorderClass(tl, tr, br, bl) { + return (tl ? 1 : 0) | (tr ? 2 : 0) | (br ? 4 : 0) | (bl ? 8 : 0); + } + exports.Marker = Marker; + +}); + +define("ace/layer/text_util", ["require", "exports", "module"], function(require, exports, module) { // Tokens for which Ace just uses a simple TextNode and does not add any special className. + var textTokens = new Set(["text", "rparen", "lparen"]); + exports.isTextToken = function(tokenType) { + return textTokens.has(tokenType); + }; + +}); + +define("ace/layer/text", ["require", "exports", "module", "ace/lib/oop", "ace/lib/dom", "ace/lib/lang", "ace/layer/lines", "ace/lib/event_emitter", "ace/config", "ace/layer/text_util"], function(require, exports, module) { + "use strict"; + var oop = require("../lib/oop"); + var dom = require("../lib/dom"); + var lang = require("../lib/lang"); + var Lines = require("./lines").Lines; + var EventEmitter = require("../lib/event_emitter").EventEmitter; + var nls = require("../config").nls; + var isTextToken = require("./text_util").isTextToken; + var Text = /** @class */ (function() { + function Text(parentEl) { + this.dom = dom; + this.element = this.dom.createElement("div"); + this.element.className = "ace_layer ace_text-layer"; + parentEl.appendChild(this.element); + this.$updateEolChar = this.$updateEolChar.bind(this); + this.$lines = new Lines(this.element); + } + Text.prototype.$updateEolChar = function() { + var doc = this.session.doc; + var unixMode = doc.getNewLineCharacter() == "\n" && doc.getNewLineMode() != "windows"; + var EOL_CHAR = unixMode ? this.EOL_CHAR_LF : this.EOL_CHAR_CRLF; + if (this.EOL_CHAR != EOL_CHAR) { + this.EOL_CHAR = EOL_CHAR; + return true; + } + }; + Text.prototype.setPadding = function(padding) { + this.$padding = padding; + this.element.style.margin = "0 " + padding + "px"; + }; + Text.prototype.getLineHeight = function() { + return this.$fontMetrics.$characterSize.height || 0; + }; + Text.prototype.getCharacterWidth = function() { + return this.$fontMetrics.$characterSize.width || 0; + }; + Text.prototype.$setFontMetrics = function(measure) { + this.$fontMetrics = measure; + this.$fontMetrics.on("changeCharacterSize", + function(e) { + this._signal("changeCharacterSize", e); + }.bind(this)); + this.$pollSizeChanges(); + }; + Text.prototype.checkForSizeChanges = function() { + this.$fontMetrics.checkForSizeChanges(); + }; + Text.prototype.$pollSizeChanges = function() { + return this.$pollSizeChangesTimer = this.$fontMetrics.$pollSizeChanges(); + }; + Text.prototype.setSession = function(session) { + this.session = session; + if (session) + this.$computeTabString(); + }; + Text.prototype.setShowInvisibles = function(showInvisibles) { + if (this.showInvisibles == showInvisibles) + return false; + this.showInvisibles = showInvisibles; + if (typeof showInvisibles == "string") { + this.showSpaces = /tab/i.test(showInvisibles); + this.showTabs = /space/i.test(showInvisibles); + this.showEOL = /eol/i.test(showInvisibles); + } else { + this.showSpaces = this.showTabs = this.showEOL = showInvisibles; + } + this.$computeTabString(); + return true; + }; + Text.prototype.setDisplayIndentGuides = function(display) { + if (this.displayIndentGuides == display) + return false; + this.displayIndentGuides = display; + this.$computeTabString(); + return true; + }; + Text.prototype.setHighlightIndentGuides = function(highlight) { + if (this.$highlightIndentGuides === highlight) + return false; + this.$highlightIndentGuides = highlight; + return highlight; + }; + Text.prototype.$computeTabString = function() { + var tabSize = this.session.getTabSize(); + this.tabSize = tabSize; + var tabStr = this.$tabStrings = [0]; + for (var i = 1; i < tabSize + 1; i++) { + if (this.showTabs) { + var span = this.dom.createElement("span"); + span.className = "ace_invisible ace_invisible_tab"; + span.textContent = lang.stringRepeat(this.TAB_CHAR, i); + tabStr.push(span); + } else { + tabStr.push(this.dom.createTextNode(lang.stringRepeat(" ", i), this.element)); + } + } + if (this.displayIndentGuides) { + this.$indentGuideRe = /\s\S| \t|\t |\s$/; + var className = "ace_indent-guide"; + var spaceClass = this.showSpaces ? " ace_invisible ace_invisible_space" : ""; + var spaceContent = this.showSpaces ? + lang.stringRepeat(this.SPACE_CHAR, this.tabSize) : + lang.stringRepeat(" ", this.tabSize); + var tabClass = this.showTabs ? " ace_invisible ace_invisible_tab" : ""; + var tabContent = this.showTabs ? + lang.stringRepeat(this.TAB_CHAR, this.tabSize) : + spaceContent; + var span = this.dom.createElement("span"); + span.className = className + spaceClass; + span.textContent = spaceContent; + this.$tabStrings[" "] = span; + var span = this.dom.createElement("span"); + span.className = className + tabClass; + span.textContent = tabContent; + this.$tabStrings["\t"] = span; + } + }; + Text.prototype.updateLines = function(config, firstRow, lastRow) { + if (this.config.lastRow != config.lastRow || + this.config.firstRow != config.firstRow) { + return this.update(config); + } + this.config = config; + var first = Math.max(firstRow, config.firstRow); + var last = Math.min(lastRow, config.lastRow); + var lineElements = this.element.childNodes; + var lineElementsIdx = 0; + for (var row = config.firstRow; row < first; row++) { + var foldLine = this.session.getFoldLine(row); + if (foldLine) { + if (foldLine.containsRow(first)) { + first = foldLine.start.row; + break; + } else { + row = foldLine.end.row; + } + } + lineElementsIdx++; + } + var heightChanged = false; + var row = first; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + while (true) { + if (row > foldStart) { + row = foldLine.end.row + 1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + if (row > last) + break; + var lineElement = lineElements[lineElementsIdx++]; + if (lineElement) { + this.dom.removeChildren(lineElement); + this.$renderLine(lineElement, row, row == foldStart ? foldLine : false); + if (heightChanged) + lineElement.style.top = this.$lines.computeLineTop(row, config, this.session) + "px"; + var height = (config.lineHeight * this.session.getRowLength(row)) + "px"; + if (lineElement.style.height != height) { + heightChanged = true; + lineElement.style.height = height; + } + } + row++; + } + if (heightChanged) { + while (lineElementsIdx < this.$lines.cells.length) { + var cell = this.$lines.cells[lineElementsIdx++]; + cell.element.style.top = this.$lines.computeLineTop(cell.row, config, this.session) + "px"; + } + } + }; + Text.prototype.scrollLines = function(config) { + var oldConfig = this.config; + this.config = config; + if (this.$lines.pageChanged(oldConfig, config)) + return this.update(config); + this.$lines.moveContainer(config); + var lastRow = config.lastRow; + var oldLastRow = oldConfig ? oldConfig.lastRow : -1; + if (!oldConfig || oldLastRow < config.firstRow) + return this.update(config); + if (lastRow < oldConfig.firstRow) + return this.update(config); + if (!oldConfig || oldConfig.lastRow < config.firstRow) + return this.update(config); + if (config.lastRow < oldConfig.firstRow) + return this.update(config); + if (oldConfig.firstRow < config.firstRow) + for (var row = this.session.getFoldedRowCount(oldConfig.firstRow, config.firstRow - 1); row > 0; row--) + this.$lines.shift(); + if (oldConfig.lastRow > config.lastRow) + for (var row = this.session.getFoldedRowCount(config.lastRow + 1, oldConfig.lastRow); row > 0; row--) + this.$lines.pop(); + if (config.firstRow < oldConfig.firstRow) { + this.$lines.unshift(this.$renderLinesFragment(config, config.firstRow, oldConfig.firstRow - 1)); + } + if (config.lastRow > oldConfig.lastRow) { + this.$lines.push(this.$renderLinesFragment(config, oldConfig.lastRow + 1, config.lastRow)); + } + this.$highlightIndentGuide(); + }; + Text.prototype.$renderLinesFragment = function(config, firstRow, lastRow) { + var fragment = []; + var row = firstRow; + var foldLine = this.session.getNextFoldLine(row); + var foldStart = foldLine ? foldLine.start.row : Infinity; + while (true) { + if (row > foldStart) { + row = foldLine.end.row + 1; + foldLine = this.session.getNextFoldLine(row, foldLine); + foldStart = foldLine ? foldLine.start.row : Infinity; + } + if (row > lastRow) + break; + var line = this.$lines.createCell(row, config, this.session); + var lineEl = line.element; + this.dom.removeChildren(lineEl); + dom.setStyle(lineEl.style, "height", this.$lines.computeLineHeight(row, config, this.session) + "px"); + dom.setStyle(lineEl.style, "top", this.$lines.computeLineTop(row, config, this.session) + "px"); + this.$renderLine(lineEl, row, row == foldStart ? foldLine : false); + if (this.$useLineGroups()) { + lineEl.className = "ace_line_group"; + } else { + lineEl.className = "ace_line"; + } + fragment.push(line); + row++; + } + return fragment; + }; + Text.prototype.update = function(config) { + this.$lines.moveContainer(config); + this.config = config; + var firstRow = config.firstRow; + var lastRow = config.lastRow; + var lines = this.$lines; + while (lines.getLength()) + lines.pop(); + lines.push(this.$renderLinesFragment(config, firstRow, lastRow)); + }; + Text.prototype.$renderToken = function(parent, screenColumn, token, value) { + var self = this; + var re = /(\t)|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\uFEFF\uFFF9-\uFFFC\u2066\u2067\u2068\u202A\u202B\u202D\u202E\u202C\u2069]+)|(\u3000)|([\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]|[\uD800-\uDBFF][\uDC00-\uDFFF])/g; + var valueFragment = this.dom.createFragment(this.element); + var m; + var i = 0; + while (m = re.exec(value)) { + var tab = m[1]; + var simpleSpace = m[2]; + var controlCharacter = m[3]; + var cjkSpace = m[4]; + var cjk = m[5]; + if (!self.showSpaces && simpleSpace) + continue; + var before = i != m.index ? value.slice(i, m.index) : ""; + i = m.index + m[0].length; + if (before) { + valueFragment.appendChild(this.dom.createTextNode(before, this.element)); + } + if (tab) { + var tabSize = self.session.getScreenTabSize(screenColumn + m.index); + valueFragment.appendChild(self.$tabStrings[tabSize].cloneNode(true)); + screenColumn += tabSize - 1; + } else if (simpleSpace) { + if (self.showSpaces) { + var span = this.dom.createElement("span"); + span.className = "ace_invisible ace_invisible_space"; + span.textContent = lang.stringRepeat(self.SPACE_CHAR, simpleSpace.length); + valueFragment.appendChild(span); + } else { + valueFragment.appendChild(this.dom.createTextNode(simpleSpace, this.element)); + } + } else if (controlCharacter) { + var span = this.dom.createElement("span"); + span.className = "ace_invisible ace_invisible_space ace_invalid"; + span.textContent = lang.stringRepeat(self.SPACE_CHAR, controlCharacter.length); + valueFragment.appendChild(span); + } else if (cjkSpace) { + screenColumn += 1; + var span = this.dom.createElement("span"); + span.style.width = (self.config.characterWidth * 2) + "px"; + span.className = self.showSpaces ? "ace_cjk ace_invisible ace_invisible_space" : "ace_cjk"; + span.textContent = self.showSpaces ? self.SPACE_CHAR : cjkSpace; + valueFragment.appendChild(span); + } else if (cjk) { + screenColumn += 1; + var span = this.dom.createElement("span"); + span.style.width = (self.config.characterWidth * 2) + "px"; + span.className = "ace_cjk"; + span.textContent = cjk; + valueFragment.appendChild(span); + } + } + valueFragment.appendChild(this.dom.createTextNode(i ? value.slice(i) : value, this.element)); + if (!isTextToken(token.type)) { + var classes = "ace_" + token.type.replace(/\./g, " ace_"); + var span = this.dom.createElement("span"); + if (token.type == "fold") { + span.style.width = (token.value.length * this.config.characterWidth) + "px"; + span.setAttribute("title", nls("inline-fold.closed.title", "Unfold code")); + } + span.className = classes; + span.appendChild(valueFragment); + parent.appendChild(span); + } else { + parent.appendChild(valueFragment); + } + return screenColumn + value.length; + }; + Text.prototype.renderIndentGuide = function(parent, value, max) { + var cols = value.search(this.$indentGuideRe); + if (cols <= 0 || cols >= max) + return value; + if (value[0] == " ") { + cols -= cols % this.tabSize; + var count = cols / this.tabSize; + for (var i = 0; i < count; i++) { + parent.appendChild(this.$tabStrings[" "].cloneNode(true)); + } + this.$highlightIndentGuide(); + return value.substr(cols); + } else if (value[0] == "\t") { + for (var i = 0; i < cols; i++) { + parent.appendChild(this.$tabStrings["\t"].cloneNode(true)); + } + this.$highlightIndentGuide(); + return value.substr(cols); + } + this.$highlightIndentGuide(); + return value; + }; + Text.prototype.$highlightIndentGuide = function() { + if (!this.$highlightIndentGuides || !this.displayIndentGuides) + return; + this.$highlightIndentGuideMarker = { + indentLevel: undefined, + start: undefined, + end: undefined, + dir: undefined + }; + var lines = this.session.doc.$lines; + if (!lines) + return; + var cursor = this.session.selection.getCursor(); + var initialIndent = /^\s*/.exec(this.session.doc.getLine(cursor.row))[0].length; + var elementIndentLevel = Math.floor(initialIndent / this.tabSize); + this.$highlightIndentGuideMarker = { + indentLevel: elementIndentLevel, + start: cursor.row + }; + var bracketHighlight = this.session.$bracketHighlight; + if (bracketHighlight) { + var ranges = this.session.$bracketHighlight.ranges; + for (var i = 0; i < ranges.length; i++) { + if (cursor.row !== ranges[i].start.row) { + this.$highlightIndentGuideMarker.end = ranges[i].start.row; + if (cursor.row > ranges[i].start.row) { + this.$highlightIndentGuideMarker.dir = -1; + } else { + this.$highlightIndentGuideMarker.dir = 1; + } + break; + } + } + } + if (!this.$highlightIndentGuideMarker.end) { + if (lines[cursor.row] !== '' && cursor.column === lines[cursor.row].length) { + this.$highlightIndentGuideMarker.dir = 1; + for (var i = cursor.row + 1; i < lines.length; i++) { + var line = lines[i]; + var currentIndent = /^\s*/.exec(line)[0].length; + if (line !== '') { + this.$highlightIndentGuideMarker.end = i; + if (currentIndent <= initialIndent) + break; + } + } + } + } + this.$renderHighlightIndentGuide(); + }; + Text.prototype.$clearActiveIndentGuide = function() { + var cells = this.$lines.cells; + for (var i = 0; i < cells.length; i++) { + var cell = cells[i]; + var childNodes = cell.element.childNodes; + if (childNodes.length > 0) { + for (var j = 0; j < childNodes.length; j++) { + if (childNodes[j].classList && childNodes[j].classList.contains("ace_indent-guide-active")) { + childNodes[j].classList.remove("ace_indent-guide-active"); + break; + } + } + } + } + }; + Text.prototype.$setIndentGuideActive = function(cell, indentLevel) { + var line = this.session.doc.getLine(cell.row); + if (line !== "") { + var childNodes = cell.element.childNodes; + if (childNodes) { + var node = childNodes[indentLevel - 1]; + if (node && node.classList && node.classList.contains("ace_indent-guide")) + node.classList.add("ace_indent-guide-active"); + } + } + }; + Text.prototype.$renderHighlightIndentGuide = function() { + if (!this.$lines) + return; + var cells = this.$lines.cells; + this.$clearActiveIndentGuide(); + var indentLevel = this.$highlightIndentGuideMarker.indentLevel; + if (indentLevel !== 0) { + if (this.$highlightIndentGuideMarker.dir === 1) { + for (var i = 0; i < cells.length; i++) { + var cell = cells[i]; + if (this.$highlightIndentGuideMarker.end && cell.row >= this.$highlightIndentGuideMarker.start + + 1) { + if (cell.row >= this.$highlightIndentGuideMarker.end) + break; + this.$setIndentGuideActive(cell, indentLevel); + } + } + } else { + for (var i = cells.length - 1; i >= 0; i--) { + var cell = cells[i]; + if (this.$highlightIndentGuideMarker.end && cell.row < this.$highlightIndentGuideMarker.start) { + if (cell.row <= this.$highlightIndentGuideMarker.end) + break; + this.$setIndentGuideActive(cell, indentLevel); + } + } + } + } + }; + Text.prototype.$createLineElement = function(parent) { + var lineEl = this.dom.createElement("div"); + lineEl.className = "ace_line"; + lineEl.style.height = this.config.lineHeight + "px"; + return lineEl; + }; + Text.prototype.$renderWrappedLine = function(parent, tokens, splits) { + var chars = 0; + var split = 0; + var splitChars = splits[0]; + var screenColumn = 0; + var lineEl = this.$createLineElement(); + parent.appendChild(lineEl); + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + var value = token.value; + if (i == 0 && this.displayIndentGuides) { + chars = value.length; + value = this.renderIndentGuide(lineEl, value, splitChars); + if (!value) + continue; + chars -= value.length; + } + if (chars + value.length < splitChars) { + screenColumn = this.$renderToken(lineEl, screenColumn, token, value); + chars += value.length; + } else { + while (chars + value.length >= splitChars) { + screenColumn = this.$renderToken(lineEl, screenColumn, token, value.substring(0, splitChars - chars)); + value = value.substring(splitChars - chars); + chars = splitChars; + lineEl = this.$createLineElement(); + parent.appendChild(lineEl); + lineEl.appendChild(this.dom.createTextNode(lang.stringRepeat("\xa0", splits.indent), this.element)); + split++; + screenColumn = 0; + splitChars = splits[split] || Number.MAX_VALUE; + } + if (value.length != 0) { + chars += value.length; + screenColumn = this.$renderToken(lineEl, screenColumn, token, value); + } + } + } + if (splits[splits.length - 1] > this.MAX_LINE_LENGTH) + this.$renderOverflowMessage(lineEl, screenColumn, null, "", true); + }; + Text.prototype.$renderSimpleLine = function(parent, tokens) { + var screenColumn = 0; + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + var value = token.value; + if (i == 0 && this.displayIndentGuides) { + value = this.renderIndentGuide(parent, value); + if (!value) + continue; + } + if (screenColumn + value.length > this.MAX_LINE_LENGTH) + return this.$renderOverflowMessage(parent, screenColumn, token, value); + screenColumn = this.$renderToken(parent, screenColumn, token, value); + } + }; + Text.prototype.$renderOverflowMessage = function(parent, screenColumn, token, value, hide) { + token && this.$renderToken(parent, screenColumn, token, value.slice(0, this.MAX_LINE_LENGTH - screenColumn)); + var overflowEl = this.dom.createElement("span"); + overflowEl.className = "ace_inline_button ace_keyword ace_toggle_wrap"; + overflowEl.textContent = hide ? "" : ""; + parent.appendChild(overflowEl); + }; + Text.prototype.$renderLine = function(parent, row, foldLine) { + if (!foldLine && foldLine != false) + foldLine = this.session.getFoldLine(row); + if (foldLine) + var tokens = this.$getFoldLineTokens(row, foldLine); + else + var tokens = this.session.getTokens(row); + var lastLineEl = parent; + if (tokens.length) { + var splits = this.session.getRowSplitData(row); + if (splits && splits.length) { + this.$renderWrappedLine(parent, tokens, splits); + var lastLineEl = parent.lastChild; + } else { + var lastLineEl = parent; + if (this.$useLineGroups()) { + lastLineEl = this.$createLineElement(); + parent.appendChild(lastLineEl); + } + this.$renderSimpleLine(lastLineEl, tokens); + } + } else if (this.$useLineGroups()) { + lastLineEl = this.$createLineElement(); + parent.appendChild(lastLineEl); + } + if (this.showEOL && lastLineEl) { + if (foldLine) + row = foldLine.end.row; + var invisibleEl = this.dom.createElement("span"); + invisibleEl.className = "ace_invisible ace_invisible_eol"; + invisibleEl.textContent = row == this.session.getLength() - 1 ? this.EOF_CHAR : this.EOL_CHAR; + lastLineEl.appendChild(invisibleEl); + } + }; + Text.prototype.$getFoldLineTokens = function(row, foldLine) { + var session = this.session; + var renderTokens = []; + + function addTokens(tokens, from, to) { + var idx = 0, + col = 0; + while ((col + tokens[idx].value.length) < from) { + col += tokens[idx].value.length; + idx++; + if (idx == tokens.length) + return; + } + if (col != from) { + var value = tokens[idx].value.substring(from - col); + if (value.length > (to - from)) + value = value.substring(0, to - from); + renderTokens.push({ + type: tokens[idx].type, + value: value + }); + col = from + value.length; + idx += 1; + } + while (col < to && idx < tokens.length) { + var value = tokens[idx].value; + if (value.length + col > to) { + renderTokens.push({ + type: tokens[idx].type, + value: value.substring(0, to - col) + }); + } else + renderTokens.push(tokens[idx]); + col += value.length; + idx += 1; + } + } + var tokens = session.getTokens(row); + foldLine.walk(function(placeholder, row, column, lastColumn, isNewRow) { + if (placeholder != null) { + renderTokens.push({ + type: "fold", + value: placeholder + }); + } else { + if (isNewRow) + tokens = session.getTokens(row); + if (tokens.length) + addTokens(tokens, lastColumn, column); + } + }, foldLine.end.row, this.session.getLine(foldLine.end.row).length); + return renderTokens; + }; + Text.prototype.$useLineGroups = function() { + return this.session.getUseWrapMode(); + }; + return Text; + }()); + Text.prototype.EOF_CHAR = "\xB6"; + Text.prototype.EOL_CHAR_LF = "\xAC"; + Text.prototype.EOL_CHAR_CRLF = "\xa4"; + Text.prototype.EOL_CHAR = Text.prototype.EOL_CHAR_LF; + Text.prototype.TAB_CHAR = "\u2014"; //"\u21E5"; + Text.prototype.SPACE_CHAR = "\xB7"; + Text.prototype.$padding = 0; + Text.prototype.MAX_LINE_LENGTH = 10000; + Text.prototype.showInvisibles = false; + Text.prototype.showSpaces = false; + Text.prototype.showTabs = false; + Text.prototype.showEOL = false; + Text.prototype.displayIndentGuides = true; + Text.prototype.$highlightIndentGuides = true; + Text.prototype.$tabStrings = []; + Text.prototype.destroy = {}; + Text.prototype.onChangeTabSize = Text.prototype.$computeTabString; + oop.implement(Text.prototype, EventEmitter); + exports.Text = Text; + +}); + +define("ace/layer/cursor", ["require", "exports", "module", "ace/lib/dom"], function(require, exports, module) { + "use strict"; + var dom = require("../lib/dom"); + var Cursor = /** @class */ (function() { + function Cursor(parentEl) { + this.element = dom.createElement("div"); + this.element.className = "ace_layer ace_cursor-layer"; + parentEl.appendChild(this.element); + this.isVisible = false; + this.isBlinking = true; + this.blinkInterval = 1000; + this.smoothBlinking = false; + this.cursors = []; + this.cursor = this.addCursor(); + dom.addCssClass(this.element, "ace_hidden-cursors"); + this.$updateCursors = this.$updateOpacity.bind(this); + } + Cursor.prototype.$updateOpacity = function(val) { + var cursors = this.cursors; + for (var i = cursors.length; i--;) + dom.setStyle(cursors[i].style, "opacity", val ? "" : "0"); + }; + Cursor.prototype.$startCssAnimation = function() { + var cursors = this.cursors; + for (var i = cursors.length; i--;) + cursors[i].style.animationDuration = this.blinkInterval + "ms"; + this.$isAnimating = true; + setTimeout(function() { + if (this.$isAnimating) { + dom.addCssClass(this.element, "ace_animate-blinking"); + } + }.bind(this)); + }; + Cursor.prototype.$stopCssAnimation = function() { + this.$isAnimating = false; + dom.removeCssClass(this.element, "ace_animate-blinking"); + }; + Cursor.prototype.setPadding = function(padding) { + this.$padding = padding; + }; + Cursor.prototype.setSession = function(session) { + this.session = session; + }; + Cursor.prototype.setBlinking = function(blinking) { + if (blinking != this.isBlinking) { + this.isBlinking = blinking; + this.restartTimer(); + } + }; + Cursor.prototype.setBlinkInterval = function(blinkInterval) { + if (blinkInterval != this.blinkInterval) { + this.blinkInterval = blinkInterval; + this.restartTimer(); + } + }; + Cursor.prototype.setSmoothBlinking = function(smoothBlinking) { + if (smoothBlinking != this.smoothBlinking) { + this.smoothBlinking = smoothBlinking; + dom.setCssClass(this.element, "ace_smooth-blinking", smoothBlinking); + this.$updateCursors(true); + this.restartTimer(); + } + }; + Cursor.prototype.addCursor = function() { + var el = dom.createElement("div"); + el.className = "ace_cursor"; + this.element.appendChild(el); + this.cursors.push(el); + return el; + }; + Cursor.prototype.removeCursor = function() { + if (this.cursors.length > 1) { + var el = this.cursors.pop(); + el.parentNode.removeChild(el); + return el; + } + }; + Cursor.prototype.hideCursor = function() { + this.isVisible = false; + dom.addCssClass(this.element, "ace_hidden-cursors"); + this.restartTimer(); + }; + Cursor.prototype.showCursor = function() { + this.isVisible = true; + dom.removeCssClass(this.element, "ace_hidden-cursors"); + this.restartTimer(); + }; + Cursor.prototype.restartTimer = function() { + var update = this.$updateCursors; + clearInterval(this.intervalId); + clearTimeout(this.timeoutId); + this.$stopCssAnimation(); + if (this.smoothBlinking) { + this.$isSmoothBlinking = false; + dom.removeCssClass(this.element, "ace_smooth-blinking"); + } + update(true); + if (!this.isBlinking || !this.blinkInterval || !this.isVisible) { + this.$stopCssAnimation(); + return; + } + if (this.smoothBlinking) { + this.$isSmoothBlinking = true; + setTimeout(function() { + if (this.$isSmoothBlinking) { + dom.addCssClass(this.element, "ace_smooth-blinking"); + } + }.bind(this)); + } + if (dom.HAS_CSS_ANIMATION) { + this.$startCssAnimation(); + } else { + var blink = /**@this{Cursor}*/ function() { + this.timeoutId = setTimeout(function() { + update(false); + }, 0.6 * this.blinkInterval); + }.bind(this); + this.intervalId = setInterval(function() { + update(true); + blink(); + }, this.blinkInterval); + blink(); + } + }; + Cursor.prototype.getPixelPosition = function(position, onScreen) { + if (!this.config || !this.session) + return { + left: 0, + top: 0 + }; + if (!position) + position = this.session.selection.getCursor(); + var pos = this.session.documentToScreenPosition(position); + var cursorLeft = this.$padding + (this.session.$bidiHandler.isBidiRow(pos.row, position.row) ? + this.session.$bidiHandler.getPosLeft(pos.column) : + pos.column * this.config.characterWidth); + var cursorTop = (pos.row - (onScreen ? this.config.firstRowScreen : 0)) * + this.config.lineHeight; + return { + left: cursorLeft, + top: cursorTop + }; + }; + Cursor.prototype.isCursorInView = function(pixelPos, config) { + return pixelPos.top >= 0 && pixelPos.top < config.maxHeight; + }; + Cursor.prototype.update = function(config) { + this.config = config; + var selections = this.session.$selectionMarkers; + var i = 0, + cursorIndex = 0; + if (selections === undefined || selections.length === 0) { + selections = [{ + cursor: null + }]; + } + for (var i = 0, n = selections.length; i < n; i++) { + var pixelPos = this.getPixelPosition(selections[i].cursor, true); + if ((pixelPos.top > config.height + config.offset || + pixelPos.top < 0) && i > 1) { + continue; + } + var element = this.cursors[cursorIndex++] || this.addCursor(); + var style = element.style; + if (!this.drawCursor) { + if (!this.isCursorInView(pixelPos, config)) { + dom.setStyle(style, "display", "none"); + } else { + dom.setStyle(style, "display", "block"); + dom.translate(element, pixelPos.left, pixelPos.top); + dom.setStyle(style, "width", Math.round(config.characterWidth) + "px"); + dom.setStyle(style, "height", config.lineHeight + "px"); + } + } else { + this.drawCursor(element, pixelPos, config, selections[i], this.session); + } + } + while (this.cursors.length > cursorIndex) + this.removeCursor(); + var overwrite = this.session.getOverwrite(); + this.$setOverwrite(overwrite); + this.$pixelPos = pixelPos; + this.restartTimer(); + }; + Cursor.prototype.$setOverwrite = function(overwrite) { + if (overwrite != this.overwrite) { + this.overwrite = overwrite; + if (overwrite) + dom.addCssClass(this.element, "ace_overwrite-cursors"); + else + dom.removeCssClass(this.element, "ace_overwrite-cursors"); + } + }; + Cursor.prototype.destroy = function() { + clearInterval(this.intervalId); + clearTimeout(this.timeoutId); + }; + return Cursor; + }()); + Cursor.prototype.$padding = 0; + Cursor.prototype.drawCursor = null; + exports.Cursor = Cursor; + +}); + +define("ace/scrollbar", ["require", "exports", "module", "ace/lib/oop", "ace/lib/dom", "ace/lib/event", "ace/lib/event_emitter"], function(require, exports, module) { + "use strict"; + var __extends = (this && this.__extends) || (function() { + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ + __proto__: [] + } + instanceof Array && function(d, b) { + d.__proto__ = b; + }) || + function(d, b) { + for (var p in b) + if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); + }; + return function(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + var oop = require("./lib/oop"); + var dom = require("./lib/dom"); + var event = require("./lib/event"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var MAX_SCROLL_H = 0x8000; + var Scrollbar = /** @class */ (function() { + function Scrollbar(parent, classSuffix) { + this.element = dom.createElement("div"); + this.element.className = "ace_scrollbar ace_scrollbar" + classSuffix; + this.inner = dom.createElement("div"); + this.inner.className = "ace_scrollbar-inner"; + this.inner.textContent = "\xa0"; + this.element.appendChild(this.inner); + parent.appendChild(this.element); + this.setVisible(false); + this.skipEvent = false; + event.addListener(this.element, "scroll", this.onScroll.bind(this)); + event.addListener(this.element, "mousedown", event.preventDefault); + } + Scrollbar.prototype.setVisible = function(isVisible) { + this.element.style.display = isVisible ? "" : "none"; + this.isVisible = isVisible; + this.coeff = 1; + }; + return Scrollbar; + }()); + oop.implement(Scrollbar.prototype, EventEmitter); + var VScrollBar = /** @class */ (function(_super) { + __extends(VScrollBar, _super); + + function VScrollBar(parent, renderer) { + var _this = _super.call(this, parent, '-v') || this; + _this.scrollTop = 0; + _this.scrollHeight = 0; + renderer.$scrollbarWidth = + _this.width = dom.scrollbarWidth(parent.ownerDocument); + _this.inner.style.width = + _this.element.style.width = (_this.width || 15) + 5 + "px"; + _this.$minWidth = 0; + return _this; + } + VScrollBar.prototype.onScroll = function() { + if (!this.skipEvent) { + this.scrollTop = this.element.scrollTop; + if (this.coeff != 1) { + var h = this.element.clientHeight / this.scrollHeight; + this.scrollTop = this.scrollTop * (1 - h) / (this.coeff - h); + } + this._emit("scroll", { + data: this.scrollTop + }); + } + this.skipEvent = false; + }; + VScrollBar.prototype.getWidth = function() { + return Math.max(this.isVisible ? this.width : 0, this.$minWidth || 0); + }; + VScrollBar.prototype.setHeight = function(height) { + this.element.style.height = height + "px"; + }; + VScrollBar.prototype.setScrollHeight = function(height) { + this.scrollHeight = height; + if (height > MAX_SCROLL_H) { + this.coeff = MAX_SCROLL_H / height; + height = MAX_SCROLL_H; + } else if (this.coeff != 1) { + this.coeff = 1; + } + this.inner.style.height = height + "px"; + }; + VScrollBar.prototype.setScrollTop = function(scrollTop) { + if (this.scrollTop != scrollTop) { + this.skipEvent = true; + this.scrollTop = scrollTop; + this.element.scrollTop = scrollTop * this.coeff; + } + }; + return VScrollBar; + }(Scrollbar)); + VScrollBar.prototype.setInnerHeight = VScrollBar.prototype.setScrollHeight; + var HScrollBar = /** @class */ (function(_super) { + __extends(HScrollBar, _super); + + function HScrollBar(parent, renderer) { + var _this = _super.call(this, parent, '-h') || this; + _this.scrollLeft = 0; + _this.height = renderer.$scrollbarWidth; + _this.inner.style.height = + _this.element.style.height = (_this.height || 15) + 5 + "px"; + return _this; + } + HScrollBar.prototype.onScroll = function() { + if (!this.skipEvent) { + this.scrollLeft = this.element.scrollLeft; + this._emit("scroll", { + data: this.scrollLeft + }); + } + this.skipEvent = false; + }; + HScrollBar.prototype.getHeight = function() { + return this.isVisible ? this.height : 0; + }; + HScrollBar.prototype.setWidth = function(width) { + this.element.style.width = width + "px"; + }; + HScrollBar.prototype.setInnerWidth = function(width) { + this.inner.style.width = width + "px"; + }; + HScrollBar.prototype.setScrollWidth = function(width) { + this.inner.style.width = width + "px"; + }; + HScrollBar.prototype.setScrollLeft = function(scrollLeft) { + if (this.scrollLeft != scrollLeft) { + this.skipEvent = true; + this.scrollLeft = this.element.scrollLeft = scrollLeft; + } + }; + return HScrollBar; + }(Scrollbar)); + exports.ScrollBar = VScrollBar; // backward compatibility + exports.ScrollBarV = VScrollBar; // backward compatibility + exports.ScrollBarH = HScrollBar; // backward compatibility + exports.VScrollBar = VScrollBar; + exports.HScrollBar = HScrollBar; + +}); + +define("ace/scrollbar_custom", ["require", "exports", "module", "ace/lib/oop", "ace/lib/dom", "ace/lib/event", "ace/lib/event_emitter"], function(require, exports, module) { + "use strict"; + var __extends = (this && this.__extends) || (function() { + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ + __proto__: [] + } + instanceof Array && function(d, b) { + d.__proto__ = b; + }) || + function(d, b) { + for (var p in b) + if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; + }; + return extendStatics(d, b); + }; + return function(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + var oop = require("./lib/oop"); + var dom = require("./lib/dom"); + var event = require("./lib/event"); + var EventEmitter = require("./lib/event_emitter").EventEmitter; + dom.importCssString(".ace_editor>.ace_sb-v div, .ace_editor>.ace_sb-h div{\n position: absolute;\n background: rgba(128, 128, 128, 0.6);\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n border: 1px solid #bbb;\n border-radius: 2px;\n z-index: 8;\n}\n.ace_editor>.ace_sb-v, .ace_editor>.ace_sb-h {\n position: absolute;\n z-index: 6;\n background: none;\n overflow: hidden!important;\n}\n.ace_editor>.ace_sb-v {\n z-index: 6;\n right: 0;\n top: 0;\n width: 12px;\n}\n.ace_editor>.ace_sb-v div {\n z-index: 8;\n right: 0;\n width: 100%;\n}\n.ace_editor>.ace_sb-h {\n bottom: 0;\n left: 0;\n height: 12px;\n}\n.ace_editor>.ace_sb-h div {\n bottom: 0;\n height: 100%;\n}\n.ace_editor>.ace_sb_grabbed {\n z-index: 8;\n background: #000;\n}", "ace_scrollbar.css", false); + var ScrollBar = /** @class */ (function() { + function ScrollBar(parent, classSuffix) { + this.element = dom.createElement("div"); + this.element.className = "ace_sb" + classSuffix; + this.inner = dom.createElement("div"); + this.inner.className = ""; + this.element.appendChild(this.inner); + this.VScrollWidth = 12; + this.HScrollHeight = 12; + parent.appendChild(this.element); + this.setVisible(false); + this.skipEvent = false; + event.addMultiMouseDownListener(this.element, [500, 300, 300], this, "onMouseDown"); + } + ScrollBar.prototype.setVisible = function(isVisible) { + this.element.style.display = isVisible ? "" : "none"; + this.isVisible = isVisible; + this.coeff = 1; + }; + return ScrollBar; + }()); + oop.implement(ScrollBar.prototype, EventEmitter); + var VScrollBar = /** @class */ (function(_super) { + __extends(VScrollBar, _super); + + function VScrollBar(parent, renderer) { + var _this = _super.call(this, parent, '-v') || this; + _this.scrollTop = 0; + _this.scrollHeight = 0; + _this.parent = parent; + _this.width = _this.VScrollWidth; + _this.renderer = renderer; + _this.inner.style.width = _this.element.style.width = (_this.width || 15) + "px"; + _this.$minWidth = 0; + return _this; + } + VScrollBar.prototype.onMouseDown = function(eType, e) { + if (eType !== "mousedown") + return; + if (event.getButton(e) !== 0 || e.detail === 2) { + return; + } + if (e.target === this.inner) { + var self = this; + var mousePageY = e.clientY; + var onMouseMove = function(e) { + mousePageY = e.clientY; + }; + var onMouseUp = function() { + clearInterval(timerId); + }; + var startY = e.clientY; + var startTop = this.thumbTop; + var onScrollInterval = function() { + if (mousePageY === undefined) + return; + var scrollTop = self.scrollTopFromThumbTop(startTop + mousePageY - startY); + if (scrollTop === self.scrollTop) + return; + self._emit("scroll", { + data: scrollTop + }); + }; + event.capture(this.inner, onMouseMove, onMouseUp); + var timerId = setInterval(onScrollInterval, 20); + return event.preventDefault(e); + } + var top = e.clientY - this.element.getBoundingClientRect().top - this.thumbHeight / 2; + this._emit("scroll", { + data: this.scrollTopFromThumbTop(top) + }); + return event.preventDefault(e); + }; + VScrollBar.prototype.getHeight = function() { + return this.height; + }; + VScrollBar.prototype.scrollTopFromThumbTop = function(thumbTop) { + var scrollTop = thumbTop * (this.pageHeight - this.viewHeight) / (this.slideHeight - this.thumbHeight); + scrollTop = scrollTop >> 0; + if (scrollTop < 0) { + scrollTop = 0; + } else if (scrollTop > this.pageHeight - this.viewHeight) { + scrollTop = this.pageHeight - this.viewHeight; + } + return scrollTop; + }; + VScrollBar.prototype.getWidth = function() { + return Math.max(this.isVisible ? this.width : 0, this.$minWidth || 0); + }; + VScrollBar.prototype.setHeight = function(height) { + this.height = Math.max(0, height); + this.slideHeight = this.height; + this.viewHeight = this.height; + this.setScrollHeight(this.pageHeight, true); + }; + VScrollBar.prototype.setScrollHeight = function(height, force) { + if (this.pageHeight === height && !force) + return; + this.pageHeight = height; + this.thumbHeight = this.slideHeight * this.viewHeight / this.pageHeight; + if (this.thumbHeight > this.slideHeight) + this.thumbHeight = this.slideHeight; + if (this.thumbHeight < 15) + this.thumbHeight = 15; + this.inner.style.height = this.thumbHeight + "px"; + if (this.scrollTop > (this.pageHeight - this.viewHeight)) { + this.scrollTop = (this.pageHeight - this.viewHeight); + if (this.scrollTop < 0) + this.scrollTop = 0; + this._emit("scroll", { + data: this.scrollTop + }); + } + }; + VScrollBar.prototype.setScrollTop = function(scrollTop) { + this.scrollTop = scrollTop; + if (scrollTop < 0) + scrollTop = 0; + this.thumbTop = scrollTop * (this.slideHeight - this.thumbHeight) / (this.pageHeight - this.viewHeight); + this.inner.style.top = this.thumbTop + "px"; + }; + return VScrollBar; + }(ScrollBar)); + VScrollBar.prototype.setInnerHeight = VScrollBar.prototype.setScrollHeight; + var HScrollBar = /** @class */ (function(_super) { + __extends(HScrollBar, _super); + + function HScrollBar(parent, renderer) { + var _this = _super.call(this, parent, '-h') || this; + _this.scrollLeft = 0; + _this.scrollWidth = 0; + _this.height = _this.HScrollHeight; + _this.inner.style.height = _this.element.style.height = (_this.height || 12) + "px"; + _this.renderer = renderer; + return _this; + } + HScrollBar.prototype.onMouseDown = function(eType, e) { + if (eType !== "mousedown") + return; + if (event.getButton(e) !== 0 || e.detail === 2) { + return; + } + if (e.target === this.inner) { + var self = this; + var mousePageX = e.clientX; + var onMouseMove = function(e) { + mousePageX = e.clientX; + }; + var onMouseUp = function() { + clearInterval(timerId); + }; + var startX = e.clientX; + var startLeft = this.thumbLeft; + var onScrollInterval = function() { + if (mousePageX === undefined) + return; + var scrollLeft = self.scrollLeftFromThumbLeft(startLeft + mousePageX - startX); + if (scrollLeft === self.scrollLeft) + return; + self._emit("scroll", { + data: scrollLeft + }); + }; + event.capture(this.inner, onMouseMove, onMouseUp); + var timerId = setInterval(onScrollInterval, 20); + return event.preventDefault(e); + } + var left = e.clientX - this.element.getBoundingClientRect().left - this.thumbWidth / 2; + this._emit("scroll", { + data: this.scrollLeftFromThumbLeft(left) + }); + return event.preventDefault(e); + }; + HScrollBar.prototype.getHeight = function() { + return this.isVisible ? this.height : 0; + }; + HScrollBar.prototype.scrollLeftFromThumbLeft = function(thumbLeft) { + var scrollLeft = thumbLeft * (this.pageWidth - this.viewWidth) / (this.slideWidth - this.thumbWidth); + scrollLeft = scrollLeft >> 0; + if (scrollLeft < 0) { + scrollLeft = 0; + } else if (scrollLeft > this.pageWidth - this.viewWidth) { + scrollLeft = this.pageWidth - this.viewWidth; + } + return scrollLeft; + }; + HScrollBar.prototype.setWidth = function(width) { + this.width = Math.max(0, width); + this.element.style.width = this.width + "px"; + this.slideWidth = this.width; + this.viewWidth = this.width; + this.setScrollWidth(this.pageWidth, true); + }; + HScrollBar.prototype.setScrollWidth = function(width, force) { + if (this.pageWidth === width && !force) + return; + this.pageWidth = width; + this.thumbWidth = this.slideWidth * this.viewWidth / this.pageWidth; + if (this.thumbWidth > this.slideWidth) + this.thumbWidth = this.slideWidth; + if (this.thumbWidth < 15) + this.thumbWidth = 15; + this.inner.style.width = this.thumbWidth + "px"; + if (this.scrollLeft > (this.pageWidth - this.viewWidth)) { + this.scrollLeft = (this.pageWidth - this.viewWidth); + if (this.scrollLeft < 0) + this.scrollLeft = 0; + this._emit("scroll", { + data: this.scrollLeft + }); + } + }; + HScrollBar.prototype.setScrollLeft = function(scrollLeft) { + this.scrollLeft = scrollLeft; + if (scrollLeft < 0) + scrollLeft = 0; + this.thumbLeft = scrollLeft * (this.slideWidth - this.thumbWidth) / (this.pageWidth - this.viewWidth); + this.inner.style.left = (this.thumbLeft) + "px"; + }; + return HScrollBar; + }(ScrollBar)); + HScrollBar.prototype.setInnerWidth = HScrollBar.prototype.setScrollWidth; + exports.ScrollBar = VScrollBar; // backward compatibility + exports.ScrollBarV = VScrollBar; // backward compatibility + exports.ScrollBarH = HScrollBar; // backward compatibility + exports.VScrollBar = VScrollBar; + exports.HScrollBar = HScrollBar; + +}); + +define("ace/renderloop", ["require", "exports", "module", "ace/lib/event"], function(require, exports, module) { + "use strict"; + var event = require("./lib/event"); + var RenderLoop = /** @class */ (function() { + function RenderLoop(onRender, win) { + this.onRender = onRender; + this.pending = false; + this.changes = 0; + this.$recursionLimit = 2; + this.window = win || window; + var _self = this; + this._flush = function(ts) { + _self.pending = false; + var changes = _self.changes; + if (changes) { + event.blockIdle(100); + _self.changes = 0; + _self.onRender(changes); + } + if (_self.changes) { + if (_self.$recursionLimit-- < 0) + return; + _self.schedule(); + } else { + _self.$recursionLimit = 2; + } + }; + } + RenderLoop.prototype.schedule = function(change) { + this.changes = this.changes | change; + if (this.changes && !this.pending) { + event.nextFrame(this._flush); + this.pending = true; + } + }; + RenderLoop.prototype.clear = function(change) { + var changes = this.changes; + this.changes = 0; + return changes; + }; + return RenderLoop; + }()); + exports.RenderLoop = RenderLoop; + +}); + +define("ace/layer/font_metrics", ["require", "exports", "module", "ace/lib/oop", "ace/lib/dom", "ace/lib/lang", "ace/lib/event", "ace/lib/useragent", "ace/lib/event_emitter"], function(require, exports, module) { + var oop = require("../lib/oop"); + var dom = require("../lib/dom"); + var lang = require("../lib/lang"); + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + var EventEmitter = require("../lib/event_emitter").EventEmitter; + var CHAR_COUNT = 512; + var USE_OBSERVER = typeof ResizeObserver == "function"; + var L = 200; + var FontMetrics = /** @class */ (function() { + function FontMetrics(parentEl) { + this.el = dom.createElement("div"); + this.$setMeasureNodeStyles(this.el.style, true); + this.$main = dom.createElement("div"); + this.$setMeasureNodeStyles(this.$main.style); + this.$measureNode = dom.createElement("div"); + this.$setMeasureNodeStyles(this.$measureNode.style); + this.el.appendChild(this.$main); + this.el.appendChild(this.$measureNode); + parentEl.appendChild(this.el); + this.$measureNode.textContent = lang.stringRepeat("X", CHAR_COUNT); + this.$characterSize = { + width: 0, + height: 0 + }; + if (USE_OBSERVER) + this.$addObserver(); + else + this.checkForSizeChanges(); + } + FontMetrics.prototype.$setMeasureNodeStyles = function(style, isRoot) { + style.width = style.height = "auto"; + style.left = style.top = "0px"; + style.visibility = "hidden"; + style.position = "absolute"; + style.whiteSpace = "pre"; + if (useragent.isIE < 8) { + style["font-family"] = "inherit"; + } else { + style.font = "inherit"; + } + style.overflow = isRoot ? "hidden" : "visible"; + }; + FontMetrics.prototype.checkForSizeChanges = function(size) { + if (size === undefined) + size = this.$measureSizes(); + if (size && (this.$characterSize.width !== size.width || this.$characterSize.height !== size.height)) { + this.$measureNode.style.fontWeight = "bold"; + var boldSize = this.$measureSizes(); + this.$measureNode.style.fontWeight = ""; + this.$characterSize = size; + this.charSizes = Object.create(null); + this.allowBoldFonts = boldSize && boldSize.width === size.width && boldSize.height === size.height; + this._emit("changeCharacterSize", { + data: size + }); + } + }; + FontMetrics.prototype.$addObserver = function() { + var self = this; + this.$observer = new window.ResizeObserver(function(e) { + self.checkForSizeChanges(); + }); + this.$observer.observe(this.$measureNode); + }; + FontMetrics.prototype.$pollSizeChanges = function() { + if (this.$pollSizeChangesTimer || this.$observer) + return this.$pollSizeChangesTimer; + var self = this; + return this.$pollSizeChangesTimer = event.onIdle(function cb() { + self.checkForSizeChanges(); + event.onIdle(cb, 500); + }, 500); + }; + FontMetrics.prototype.setPolling = function(val) { + if (val) { + this.$pollSizeChanges(); + } else if (this.$pollSizeChangesTimer) { + clearInterval(this.$pollSizeChangesTimer); + this.$pollSizeChangesTimer = 0; + } + }; + FontMetrics.prototype.$measureSizes = function(node) { + var size = { + height: (node || this.$measureNode).clientHeight, + width: (node || this.$measureNode).clientWidth / CHAR_COUNT + }; + if (size.width === 0 || size.height === 0) + return null; + return size; + }; + FontMetrics.prototype.$measureCharWidth = function(ch) { + this.$main.textContent = lang.stringRepeat(ch, CHAR_COUNT); + var rect = this.$main.getBoundingClientRect(); + return rect.width / CHAR_COUNT; + }; + FontMetrics.prototype.getCharacterWidth = function(ch) { + var w = this.charSizes[ch]; + if (w === undefined) { + w = this.charSizes[ch] = this.$measureCharWidth(ch) / this.$characterSize.width; + } + return w; + }; + FontMetrics.prototype.destroy = function() { + clearInterval(this.$pollSizeChangesTimer); + if (this.$observer) + this.$observer.disconnect(); + if (this.el && this.el.parentNode) + this.el.parentNode.removeChild(this.el); + }; + FontMetrics.prototype.$getZoom = function(element) { + if (!element || !element.parentElement) + return 1; + return (Number(window.getComputedStyle(element)["zoom"]) || 1) * this.$getZoom(element.parentElement); + }; + FontMetrics.prototype.$initTransformMeasureNodes = function() { + var t = function(t, l) { + return ["div", { + style: "position: absolute;top:" + t + "px;left:" + l + "px;" + }]; + }; + this.els = dom.buildDom([t(0, 0), t(L, 0), t(0, L), t(L, L)], this.el); + }; + FontMetrics.prototype.transformCoordinates = function(clientPos, elPos) { + if (clientPos) { + var zoom = this.$getZoom(this.el); + clientPos = mul(1 / zoom, clientPos); + } + + function solve(l1, l2, r) { + var det = l1[1] * l2[0] - l1[0] * l2[1]; + return [ + (-l2[1] * r[0] + l2[0] * r[1]) / det, + (+l1[1] * r[0] - l1[0] * r[1]) / det + ]; + } + + function sub(a, b) { + return [a[0] - b[0], a[1] - b[1]]; + } + + function add(a, b) { + return [a[0] + b[0], a[1] + b[1]]; + } + + function mul(a, b) { + return [a * b[0], a * b[1]]; + } + if (!this.els) + this.$initTransformMeasureNodes(); + + function p(el) { + var r = el.getBoundingClientRect(); + return [r.left, r.top]; + } + var a = p(this.els[0]); + var b = p(this.els[1]); + var c = p(this.els[2]); + var d = p(this.els[3]); + var h = solve(sub(d, b), sub(d, c), sub(add(b, c), add(d, a))); + var m1 = mul(1 + h[0], sub(b, a)); + var m2 = mul(1 + h[1], sub(c, a)); + if (elPos) { + var x = elPos; + var k = h[0] * x[0] / L + h[1] * x[1] / L + 1; + var ut = add(mul(x[0], m1), mul(x[1], m2)); + return add(mul(1 / k / L, ut), a); + } + var u = sub(clientPos, a); + var f = solve(sub(m1, mul(h[0], u)), sub(m2, mul(h[1], u)), u); + return mul(L, f); + }; + return FontMetrics; + }()); + FontMetrics.prototype.$characterSize = { + width: 0, + height: 0 + }; + oop.implement(FontMetrics.prototype, EventEmitter); + exports.FontMetrics = FontMetrics; + +}); + +define("ace/css/editor-css", ["require", "exports", "module"], function(require, exports, module) { + /* + styles = [] + for (var i = 1; i < 16; i++) { + styles.push(".ace_br" + i + "{" + ( + ["top-left", "top-right", "bottom-right", "bottom-left"] + ).map(function(x, j) { + return i & (1< .ace_line, .ace_text-layer > .ace_line_group {\n contain: style size layout;\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n}\n\n.ace_hidpi .ace_text-layer,\n.ace_hidpi .ace_gutter-layer,\n.ace_hidpi .ace_content,\n.ace_hidpi .ace_gutter {\n contain: strict;\n}\n.ace_hidpi .ace_text-layer > .ace_line, \n.ace_hidpi .ace_text-layer > .ace_line_group {\n contain: strict;\n}\n\n.ace_cjk {\n display: inline-block;\n text-align: center;\n}\n\n.ace_cursor-layer {\n z-index: 4;\n}\n\n.ace_cursor {\n z-index: 4;\n position: absolute;\n box-sizing: border-box;\n border-left: 2px solid;\n /* workaround for smooth cursor repaintng whole screen in chrome */\n transform: translatez(0);\n}\n\n.ace_multiselect .ace_cursor {\n border-left-width: 1px;\n}\n\n.ace_slim-cursors .ace_cursor {\n border-left-width: 1px;\n}\n\n.ace_overwrite-cursors .ace_cursor {\n border-left-width: 0;\n border-bottom: 1px solid;\n}\n\n.ace_hidden-cursors .ace_cursor {\n opacity: 0.2;\n}\n\n.ace_hasPlaceholder .ace_hidden-cursors .ace_cursor {\n opacity: 0;\n}\n\n.ace_smooth-blinking .ace_cursor {\n transition: opacity 0.18s;\n}\n\n.ace_animate-blinking .ace_cursor {\n animation-duration: 1000ms;\n animation-timing-function: step-end;\n animation-name: blink-ace-animate;\n animation-iteration-count: infinite;\n}\n\n.ace_animate-blinking.ace_smooth-blinking .ace_cursor {\n animation-duration: 1000ms;\n animation-timing-function: ease-in-out;\n animation-name: blink-ace-animate-smooth;\n}\n \n@keyframes blink-ace-animate {\n from, to { opacity: 1; }\n 60% { opacity: 0; }\n}\n\n@keyframes blink-ace-animate-smooth {\n from, to { opacity: 1; }\n 45% { opacity: 1; }\n 60% { opacity: 0; }\n 85% { opacity: 0; }\n}\n\n.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {\n position: absolute;\n z-index: 3;\n}\n\n.ace_marker-layer .ace_selection {\n position: absolute;\n z-index: 5;\n}\n\n.ace_marker-layer .ace_bracket {\n position: absolute;\n z-index: 6;\n}\n\n.ace_marker-layer .ace_error_bracket {\n position: absolute;\n border-bottom: 1px solid #DE5555;\n border-radius: 0;\n}\n\n.ace_marker-layer .ace_active-line {\n position: absolute;\n z-index: 2;\n}\n\n.ace_marker-layer .ace_selected-word {\n position: absolute;\n z-index: 4;\n box-sizing: border-box;\n}\n\n.ace_line .ace_fold {\n box-sizing: border-box;\n\n display: inline-block;\n height: 11px;\n margin-top: -2px;\n vertical-align: middle;\n\n background-image:\n url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII=\"),\n url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=\");\n background-repeat: no-repeat, repeat-x;\n background-position: center center, top left;\n color: transparent;\n\n border: 1px solid black;\n border-radius: 2px;\n\n cursor: pointer;\n pointer-events: auto;\n}\n\n.ace_dark .ace_fold {\n}\n\n.ace_fold:hover{\n background-image:\n url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII=\"),\n url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC\");\n}\n\n.ace_tooltip {\n background-color: #f5f5f5;\n border: 1px solid gray;\n border-radius: 1px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n color: black;\n max-width: 100%;\n padding: 3px 4px;\n position: fixed;\n z-index: 999999;\n box-sizing: border-box;\n cursor: default;\n white-space: pre-wrap;\n word-wrap: break-word;\n line-height: normal;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n pointer-events: none;\n overflow: auto;\n max-width: min(60em, 66vw);\n overscroll-behavior: contain;\n}\n.ace_tooltip pre {\n white-space: pre-wrap;\n}\n\n.ace_tooltip.ace_dark {\n background-color: #636363;\n color: #fff;\n}\n\n.ace_tooltip:focus {\n outline: 1px solid #5E9ED6;\n}\n\n.ace_icon {\n display: inline-block;\n width: 18px;\n vertical-align: top;\n}\n\n.ace_icon_svg {\n display: inline-block;\n width: 12px;\n vertical-align: top;\n -webkit-mask-repeat: no-repeat;\n -webkit-mask-size: 12px;\n -webkit-mask-position: center;\n}\n\n.ace_folding-enabled > .ace_gutter-cell, .ace_folding-enabled > .ace_gutter-cell_svg-icons {\n padding-right: 13px;\n}\n\n.ace_fold-widget {\n box-sizing: border-box;\n\n margin: 0 -12px 0 1px;\n display: none;\n width: 11px;\n vertical-align: top;\n\n background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==\");\n background-repeat: no-repeat;\n background-position: center;\n\n border-radius: 3px;\n \n border: 1px solid transparent;\n cursor: pointer;\n}\n\n.ace_folding-enabled .ace_fold-widget {\n display: inline-block; \n}\n\n.ace_fold-widget.ace_end {\n background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==\");\n}\n\n.ace_fold-widget.ace_closed {\n background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==\");\n}\n\n.ace_fold-widget:hover {\n border: 1px solid rgba(0, 0, 0, 0.3);\n background-color: rgba(255, 255, 255, 0.2);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);\n}\n\n.ace_fold-widget:active {\n border: 1px solid rgba(0, 0, 0, 0.4);\n background-color: rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);\n}\n/**\n * Dark version for fold widgets\n */\n.ace_dark .ace_fold-widget {\n background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC\");\n}\n.ace_dark .ace_fold-widget.ace_end {\n background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==\");\n}\n.ace_dark .ace_fold-widget.ace_closed {\n background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==\");\n}\n.ace_dark .ace_fold-widget:hover {\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\n background-color: rgba(255, 255, 255, 0.1);\n}\n.ace_dark .ace_fold-widget:active {\n box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);\n}\n\n.ace_inline_button {\n border: 1px solid lightgray;\n display: inline-block;\n margin: -1px 8px;\n padding: 0 5px;\n pointer-events: auto;\n cursor: pointer;\n}\n.ace_inline_button:hover {\n border-color: gray;\n background: rgba(200,200,200,0.2);\n display: inline-block;\n pointer-events: auto;\n}\n\n.ace_fold-widget.ace_invalid {\n background-color: #FFB4B4;\n border-color: #DE5555;\n}\n\n.ace_fade-fold-widgets .ace_fold-widget {\n transition: opacity 0.4s ease 0.05s;\n opacity: 0;\n}\n\n.ace_fade-fold-widgets:hover .ace_fold-widget {\n transition: opacity 0.05s ease 0.05s;\n opacity:1;\n}\n\n.ace_underline {\n text-decoration: underline;\n}\n\n.ace_bold {\n font-weight: bold;\n}\n\n.ace_nobold .ace_bold {\n font-weight: normal;\n}\n\n.ace_italic {\n font-style: italic;\n}\n\n\n.ace_error-marker {\n background-color: rgba(255, 0, 0,0.2);\n position: absolute;\n z-index: 9;\n}\n\n.ace_highlight-marker {\n background-color: rgba(255, 255, 0,0.2);\n position: absolute;\n z-index: 8;\n}\n\n.ace_mobile-menu {\n position: absolute;\n line-height: 1.5;\n border-radius: 4px;\n -ms-user-select: none;\n -moz-user-select: none;\n -webkit-user-select: none;\n user-select: none;\n background: white;\n box-shadow: 1px 3px 2px grey;\n border: 1px solid #dcdcdc;\n color: black;\n}\n.ace_dark > .ace_mobile-menu {\n background: #333;\n color: #ccc;\n box-shadow: 1px 3px 2px grey;\n border: 1px solid #444;\n\n}\n.ace_mobile-button {\n padding: 2px;\n cursor: pointer;\n overflow: hidden;\n}\n.ace_mobile-button:hover {\n background-color: #eee;\n opacity:1;\n}\n.ace_mobile-button:active {\n background-color: #ddd;\n}\n\n.ace_placeholder {\n position: relative;\n font-family: arial;\n transform: scale(0.9);\n transform-origin: left;\n white-space: pre;\n opacity: 0.7;\n margin: 0 10px;\n z-index: 1;\n}\n\n.ace_ghost_text {\n opacity: 0.5;\n font-style: italic;\n}\n\n.ace_ghost_text_container > div {\n white-space: pre;\n}\n\n.ghost_text_line_wrapped::after {\n content: \"\u21A9\";\n position: absolute;\n}\n\n.ace_lineWidgetContainer.ace_ghost_text {\n margin: 0px 4px\n}\n\n.ace_screenreader-only {\n position:absolute;\n left:-10000px;\n top:auto;\n width:1px;\n height:1px;\n overflow:hidden;\n}\n\n.ace_hidden_token {\n display: none;\n}"; + +}); + +define("ace/layer/decorators", ["require", "exports", "module", "ace/lib/dom", "ace/lib/oop", "ace/lib/event_emitter"], function(require, exports, module) { + "use strict"; + var dom = require("../lib/dom"); + var oop = require("../lib/oop"); + var EventEmitter = require("../lib/event_emitter").EventEmitter; + var Decorator = /** @class */ (function() { + function Decorator(parent, renderer) { + this.canvas = dom.createElement("canvas"); + this.renderer = renderer; + this.pixelRatio = 1; + this.maxHeight = renderer.layerConfig.maxHeight; + this.lineHeight = renderer.layerConfig.lineHeight; + this.canvasHeight = parent.parent.scrollHeight; + this.heightRatio = this.canvasHeight / this.maxHeight; + this.canvasWidth = parent.width; + this.minDecorationHeight = (2 * this.pixelRatio) | 0; + this.halfMinDecorationHeight = (this.minDecorationHeight / 2) | 0; + this.canvas.width = this.canvasWidth; + this.canvas.height = this.canvasHeight; + this.canvas.style.top = 0 + "px"; + this.canvas.style.right = 0 + "px"; + this.canvas.style.zIndex = 7 + "px"; + this.canvas.style.position = "absolute"; + this.colors = {}; + this.colors.dark = { + "error": "rgba(255, 18, 18, 1)", + "warning": "rgba(18, 136, 18, 1)", + "info": "rgba(18, 18, 136, 1)" + }; + this.colors.light = { + "error": "rgb(255,51,51)", + "warning": "rgb(32,133,72)", + "info": "rgb(35,68,138)" + }; + parent.element.appendChild(this.canvas); + } + Decorator.prototype.$updateDecorators = function(config) { + var colors = (this.renderer.theme.isDark === true) ? this.colors.dark : this.colors.light; + if (config) { + this.maxHeight = config.maxHeight; + this.lineHeight = config.lineHeight; + this.canvasHeight = config.height; + var allLineHeight = (config.lastRow + 1) * this.lineHeight; + if (allLineHeight < this.canvasHeight) { + this.heightRatio = 1; + } else { + this.heightRatio = this.canvasHeight / this.maxHeight; + } + } + var ctx = this.canvas.getContext("2d"); + + function compare(a, b) { + if (a.priority < b.priority) + return -1; + if (a.priority > b.priority) + return 1; + return 0; + } + var annotations = this.renderer.session.$annotations; + ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + if (annotations) { + var priorities = { + "info": 1, + "warning": 2, + "error": 3 + }; + annotations.forEach(function(item) { + item.priority = priorities[item.type] || null; + }); + annotations = annotations.sort(compare); + var foldData = this.renderer.session.$foldData; + for (var i = 0; i < annotations.length; i++) { + var row = annotations[i].row; + var compensateFold = this.compensateFoldRows(row, foldData); + var currentY = Math.round((row - compensateFold) * this.lineHeight * this.heightRatio); + var y1 = Math.round(((row - compensateFold) * this.lineHeight * this.heightRatio)); + var y2 = Math.round((((row - compensateFold) * this.lineHeight + this.lineHeight) * this.heightRatio)); + var height = y2 - y1; + if (height < this.minDecorationHeight) { + var yCenter = ((y1 + y2) / 2) | 0; + if (yCenter < this.halfMinDecorationHeight) { + yCenter = this.halfMinDecorationHeight; + } else if (yCenter + this.halfMinDecorationHeight > this.canvasHeight) { + yCenter = this.canvasHeight - this.halfMinDecorationHeight; + } + y1 = Math.round(yCenter - this.halfMinDecorationHeight); + y2 = Math.round(yCenter + this.halfMinDecorationHeight); + } + ctx.fillStyle = colors[annotations[i].type] || null; + ctx.fillRect(0, currentY, this.canvasWidth, y2 - y1); + } + } + var cursor = this.renderer.session.selection.getCursor(); + if (cursor) { + var compensateFold = this.compensateFoldRows(cursor.row, foldData); + var currentY = Math.round((cursor.row - compensateFold) * this.lineHeight * this.heightRatio); + ctx.fillStyle = "rgba(0, 0, 0, 0.5)"; + ctx.fillRect(0, currentY, this.canvasWidth, 2); + } + }; + Decorator.prototype.compensateFoldRows = function(row, foldData) { + var compensateFold = 0; + if (foldData && foldData.length > 0) { + for (var j = 0; j < foldData.length; j++) { + if (row > foldData[j].start.row && row < foldData[j].end.row) { + compensateFold += row - foldData[j].start.row; + } else if (row >= foldData[j].end.row) { + compensateFold += foldData[j].end.row - foldData[j].start.row; + } + } + } + return compensateFold; + }; + return Decorator; + }()); + oop.implement(Decorator.prototype, EventEmitter); + exports.Decorator = Decorator; + +}); + +define("ace/virtual_renderer", ["require", "exports", "module", "ace/lib/oop", "ace/lib/dom", "ace/lib/lang", "ace/config", "ace/layer/gutter", "ace/layer/marker", "ace/layer/text", "ace/layer/cursor", "ace/scrollbar", "ace/scrollbar", "ace/scrollbar_custom", "ace/scrollbar_custom", "ace/renderloop", "ace/layer/font_metrics", "ace/lib/event_emitter", "ace/css/editor-css", "ace/layer/decorators", "ace/lib/useragent", "ace/layer/text_util"], function(require, exports, module) { + "use strict"; + var oop = require("./lib/oop"); + var dom = require("./lib/dom"); + var lang = require("./lib/lang"); + var config = require("./config"); + var GutterLayer = require("./layer/gutter").Gutter; + var MarkerLayer = require("./layer/marker").Marker; + var TextLayer = require("./layer/text").Text; + var CursorLayer = require("./layer/cursor").Cursor; + var HScrollBar = require("./scrollbar").HScrollBar; + var VScrollBar = require("./scrollbar").VScrollBar; + var HScrollBarCustom = require("./scrollbar_custom").HScrollBar; + var VScrollBarCustom = require("./scrollbar_custom").VScrollBar; + var RenderLoop = require("./renderloop").RenderLoop; + var FontMetrics = require("./layer/font_metrics").FontMetrics; + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var editorCss = require("./css/editor-css"); + var Decorator = require("./layer/decorators").Decorator; + var useragent = require("./lib/useragent"); + var isTextToken = require("./layer/text_util").isTextToken; + dom.importCssString(editorCss, "ace_editor.css", false); + var VirtualRenderer = /** @class */ (function() { + function VirtualRenderer(container, theme) { + var _self = this; + this.container = container || dom.createElement("div"); + dom.addCssClass(this.container, "ace_editor"); + if (dom.HI_DPI) + dom.addCssClass(this.container, "ace_hidpi"); + this.setTheme(theme); + if (config.get("useStrictCSP") == null) + config.set("useStrictCSP", false); + this.$gutter = dom.createElement("div"); + this.$gutter.className = "ace_gutter"; + this.container.appendChild(this.$gutter); + this.$gutter.setAttribute("aria-hidden", "true"); + this.scroller = dom.createElement("div"); + this.scroller.className = "ace_scroller"; + this.container.appendChild(this.scroller); + this.content = dom.createElement("div"); + this.content.className = "ace_content"; + this.scroller.appendChild(this.content); + this.$gutterLayer = new GutterLayer(this.$gutter); + this.$gutterLayer.on("changeGutterWidth", this.onGutterResize.bind(this)); + this.$markerBack = new MarkerLayer(this.content); + var textLayer = this.$textLayer = new TextLayer(this.content); + this.canvas = textLayer.element; + this.$markerFront = new MarkerLayer(this.content); + this.$cursorLayer = new CursorLayer(this.content); + this.$horizScroll = false; + this.$vScroll = false; + this.scrollBar = + this.scrollBarV = new VScrollBar(this.container, this); + this.scrollBarH = new HScrollBar(this.container, this); + this.scrollBarV.on("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollTop(e.data - _self.scrollMargin.top); + }); + this.scrollBarH.on("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollLeft(e.data - _self.scrollMargin.left); + }); + this.scrollTop = 0; + this.scrollLeft = 0; + this.cursorPos = { + row: 0, + column: 0 + }; + this.$fontMetrics = new FontMetrics(this.container); + this.$textLayer.$setFontMetrics(this.$fontMetrics); + this.$textLayer.on("changeCharacterSize", function(e) { + _self.updateCharacterSize(); + _self.onResize(true, _self.gutterWidth, _self.$size.width, _self.$size.height); + _self._signal("changeCharacterSize", e); + }); + this.$size = { + width: 0, + height: 0, + scrollerHeight: 0, + scrollerWidth: 0, + $dirty: true + }; + this.layerConfig = { + width: 1, + padding: 0, + firstRow: 0, + firstRowScreen: 0, + lastRow: 0, + lineHeight: 0, + characterWidth: 0, + minHeight: 1, + maxHeight: 1, + offset: 0, + height: 1, + gutterOffset: 1 + }; + this.scrollMargin = { + left: 0, + right: 0, + top: 0, + bottom: 0, + v: 0, + h: 0 + }; + this.margin = { + left: 0, + right: 0, + top: 0, + bottom: 0, + v: 0, + h: 0 + }; + this.$keepTextAreaAtCursor = !useragent.isIOS; + this.$loop = new RenderLoop(this.$renderChanges.bind(this), this.container.ownerDocument.defaultView); + this.$loop.schedule(this.CHANGE_FULL); + this.updateCharacterSize(); + this.setPadding(4); + this.$addResizeObserver(); + config.resetOptions(this); + config._signal("renderer", this); + } + VirtualRenderer.prototype.updateCharacterSize = function() { + if (this.$textLayer.allowBoldFonts != this.$allowBoldFonts) { + this.$allowBoldFonts = this.$textLayer.allowBoldFonts; + this.setStyle("ace_nobold", !this.$allowBoldFonts); + } + this.layerConfig.characterWidth = + this.characterWidth = this.$textLayer.getCharacterWidth(); + this.layerConfig.lineHeight = + this.lineHeight = this.$textLayer.getLineHeight(); + this.$updatePrintMargin(); + dom.setStyle(this.scroller.style, "line-height", this.lineHeight + "px"); + }; + VirtualRenderer.prototype.setSession = function(session) { + if (this.session) + this.session.doc.off("changeNewLineMode", this.onChangeNewLineMode); + this.session = session; + if (session && this.scrollMargin.top && session.getScrollTop() <= 0) + session.setScrollTop(-this.scrollMargin.top); + this.$cursorLayer.setSession(session); + this.$markerBack.setSession(session); + this.$markerFront.setSession(session); + this.$gutterLayer.setSession(session); + this.$textLayer.setSession(session); + if (!session) + return; + this.$loop.schedule(this.CHANGE_FULL); + this.session.$setFontMetrics(this.$fontMetrics); + this.scrollBarH.scrollLeft = this.scrollBarV.scrollTop = null; + this.onChangeNewLineMode = this.onChangeNewLineMode.bind(this); + this.onChangeNewLineMode(); + this.session.doc.on("changeNewLineMode", this.onChangeNewLineMode); + }; + VirtualRenderer.prototype.updateLines = function(firstRow, lastRow, force) { + if (lastRow === undefined) + lastRow = Infinity; + if (!this.$changedLines) { + this.$changedLines = { + firstRow: firstRow, + lastRow: lastRow + }; + } else { + if (this.$changedLines.firstRow > firstRow) + this.$changedLines.firstRow = firstRow; + if (this.$changedLines.lastRow < lastRow) + this.$changedLines.lastRow = lastRow; + } + if (this.$changedLines.lastRow < this.layerConfig.firstRow) { + if (force) + this.$changedLines.lastRow = this.layerConfig.lastRow; + else + return; + } + if (this.$changedLines.firstRow > this.layerConfig.lastRow) + return; + this.$loop.schedule(this.CHANGE_LINES); + }; + VirtualRenderer.prototype.onChangeNewLineMode = function() { + this.$loop.schedule(this.CHANGE_TEXT); + this.$textLayer.$updateEolChar(); + this.session.$bidiHandler.setEolChar(this.$textLayer.EOL_CHAR); + }; + VirtualRenderer.prototype.onChangeTabSize = function() { + this.$loop.schedule(this.CHANGE_TEXT | this.CHANGE_MARKER); + this.$textLayer.onChangeTabSize(); + }; + VirtualRenderer.prototype.updateText = function() { + this.$loop.schedule(this.CHANGE_TEXT); + }; + VirtualRenderer.prototype.updateFull = function(force) { + if (force) + this.$renderChanges(this.CHANGE_FULL, true); + else + this.$loop.schedule(this.CHANGE_FULL); + }; + VirtualRenderer.prototype.updateFontSize = function() { + this.$textLayer.checkForSizeChanges(); + }; + VirtualRenderer.prototype.$updateSizeAsync = function() { + if (this.$loop.pending) + this.$size.$dirty = true; + else + this.onResize(); + }; + VirtualRenderer.prototype.onResize = function(force, gutterWidth, width, height) { + if (this.resizing > 2) + return; + else if (this.resizing > 0) + this.resizing++; + else + this.resizing = force ? 1 : 0; + var el = this.container; + if (!height) + height = el.clientHeight || el.scrollHeight; + if (!height && this.$maxLines && this.lineHeight > 1) { + if (!el.style.height || el.style.height == "0px") { + el.style.height = "1px"; + height = el.clientHeight || el.scrollHeight; + } + } + if (!width) + width = el.clientWidth || el.scrollWidth; + var changes = this.$updateCachedSize(force, gutterWidth, width, height); + if (this.$resizeTimer) + this.$resizeTimer.cancel(); + if (!this.$size.scrollerHeight || (!width && !height)) + return this.resizing = 0; + if (force) + this.$gutterLayer.$padding = null; + if (force) + this.$renderChanges(changes | this.$changes, true); + else + this.$loop.schedule(changes | this.$changes); + if (this.resizing) + this.resizing = 0; + this.scrollBarH.scrollLeft = this.scrollBarV.scrollTop = null; + if (this.$customScrollbar) { + this.$updateCustomScrollbar(true); + } + }; + VirtualRenderer.prototype.$updateCachedSize = function(force, gutterWidth, width, height) { + height -= (this.$extraHeight || 0); + var changes = 0; + var size = this.$size; + var oldSize = { + width: size.width, + height: size.height, + scrollerHeight: size.scrollerHeight, + scrollerWidth: size.scrollerWidth + }; + if (height && (force || size.height != height)) { + size.height = height; + changes |= this.CHANGE_SIZE; + size.scrollerHeight = size.height; + if (this.$horizScroll) + size.scrollerHeight -= this.scrollBarH.getHeight(); + this.scrollBarV.setHeight(size.scrollerHeight); + this.scrollBarV.element.style.bottom = this.scrollBarH.getHeight() + "px"; + changes = changes | this.CHANGE_SCROLL; + } + if (width && (force || size.width != width)) { + changes |= this.CHANGE_SIZE; + size.width = width; + if (gutterWidth == null) + gutterWidth = this.$showGutter ? this.$gutter.offsetWidth : 0; + this.gutterWidth = gutterWidth; + dom.setStyle(this.scrollBarH.element.style, "left", gutterWidth + "px"); + dom.setStyle(this.scroller.style, "left", gutterWidth + this.margin.left + "px"); + size.scrollerWidth = Math.max(0, width - gutterWidth - this.scrollBarV.getWidth() - this.margin.h); + dom.setStyle(this.$gutter.style, "left", this.margin.left + "px"); + var right = this.scrollBarV.getWidth() + "px"; + dom.setStyle(this.scrollBarH.element.style, "right", right); + dom.setStyle(this.scroller.style, "right", right); + dom.setStyle(this.scroller.style, "bottom", this.scrollBarH.getHeight()); + this.scrollBarH.setWidth(size.scrollerWidth); + if (this.session && this.session.getUseWrapMode() && this.adjustWrapLimit() || force) { + changes |= this.CHANGE_FULL; + } + } + size.$dirty = !width || !height; + if (changes) + this._signal("resize", oldSize); + return changes; + }; + VirtualRenderer.prototype.onGutterResize = function(width) { + var gutterWidth = this.$showGutter ? width : 0; + if (gutterWidth != this.gutterWidth) + this.$changes |= this.$updateCachedSize(true, gutterWidth, this.$size.width, this.$size.height); + if (this.session.getUseWrapMode() && this.adjustWrapLimit()) { + this.$loop.schedule(this.CHANGE_FULL); + } else if (this.$size.$dirty) { + this.$loop.schedule(this.CHANGE_FULL); + } else { + this.$computeLayerConfig(); + } + }; + VirtualRenderer.prototype.adjustWrapLimit = function() { + var availableWidth = this.$size.scrollerWidth - this.$padding * 2; + var limit = Math.floor(availableWidth / this.characterWidth); + return this.session.adjustWrapLimit(limit, this.$showPrintMargin && this.$printMarginColumn); + }; + VirtualRenderer.prototype.setAnimatedScroll = function(shouldAnimate) { + this.setOption("animatedScroll", shouldAnimate); + }; + VirtualRenderer.prototype.getAnimatedScroll = function() { + return this.$animatedScroll; + }; + VirtualRenderer.prototype.setShowInvisibles = function(showInvisibles) { + this.setOption("showInvisibles", showInvisibles); + this.session.$bidiHandler.setShowInvisibles(showInvisibles); + }; + VirtualRenderer.prototype.getShowInvisibles = function() { + return this.getOption("showInvisibles"); + }; + VirtualRenderer.prototype.getDisplayIndentGuides = function() { + return this.getOption("displayIndentGuides"); + }; + VirtualRenderer.prototype.setDisplayIndentGuides = function(display) { + this.setOption("displayIndentGuides", display); + }; + VirtualRenderer.prototype.getHighlightIndentGuides = function() { + return this.getOption("highlightIndentGuides"); + }; + VirtualRenderer.prototype.setHighlightIndentGuides = function(highlight) { + this.setOption("highlightIndentGuides", highlight); + }; + VirtualRenderer.prototype.setShowPrintMargin = function(showPrintMargin) { + this.setOption("showPrintMargin", showPrintMargin); + }; + VirtualRenderer.prototype.getShowPrintMargin = function() { + return this.getOption("showPrintMargin"); + }; + VirtualRenderer.prototype.setPrintMarginColumn = function(printMarginColumn) { + this.setOption("printMarginColumn", printMarginColumn); + }; + VirtualRenderer.prototype.getPrintMarginColumn = function() { + return this.getOption("printMarginColumn"); + }; + VirtualRenderer.prototype.getShowGutter = function() { + return this.getOption("showGutter"); + }; + VirtualRenderer.prototype.setShowGutter = function(show) { + return this.setOption("showGutter", show); + }; + VirtualRenderer.prototype.getFadeFoldWidgets = function() { + return this.getOption("fadeFoldWidgets"); + }; + VirtualRenderer.prototype.setFadeFoldWidgets = function(show) { + this.setOption("fadeFoldWidgets", show); + }; + VirtualRenderer.prototype.setHighlightGutterLine = function(shouldHighlight) { + this.setOption("highlightGutterLine", shouldHighlight); + }; + VirtualRenderer.prototype.getHighlightGutterLine = function() { + return this.getOption("highlightGutterLine"); + }; + VirtualRenderer.prototype.$updatePrintMargin = function() { + if (!this.$showPrintMargin && !this.$printMarginEl) + return; + if (!this.$printMarginEl) { + var containerEl = dom.createElement("div"); + containerEl.className = "ace_layer ace_print-margin-layer"; + this.$printMarginEl = dom.createElement("div"); + this.$printMarginEl.className = "ace_print-margin"; + containerEl.appendChild(this.$printMarginEl); + this.content.insertBefore(containerEl, this.content.firstChild); + } + var style = this.$printMarginEl.style; + style.left = Math.round(this.characterWidth * this.$printMarginColumn + this.$padding) + "px"; + style.visibility = this.$showPrintMargin ? "visible" : "hidden"; + if (this.session && this.session.$wrap == -1) + this.adjustWrapLimit(); + }; + VirtualRenderer.prototype.getContainerElement = function() { + return this.container; + }; + VirtualRenderer.prototype.getMouseEventTarget = function() { + return this.scroller; + }; + VirtualRenderer.prototype.getTextAreaContainer = function() { + return this.container; + }; + VirtualRenderer.prototype.$moveTextAreaToCursor = function() { + if (this.$isMousePressed) + return; + var style = this.textarea.style; + var composition = this.$composition; + if (!this.$keepTextAreaAtCursor && !composition) { + dom.translate(this.textarea, -100, 0); + return; + } + var pixelPos = this.$cursorLayer.$pixelPos; + if (!pixelPos) + return; + if (composition && composition.markerRange) + pixelPos = this.$cursorLayer.getPixelPosition(composition.markerRange.start, true); + var config = this.layerConfig; + var posTop = pixelPos.top; + var posLeft = pixelPos.left; + posTop -= config.offset; + var h = composition && composition.useTextareaForIME || useragent.isMobile ? this.lineHeight : 1; + if (posTop < 0 || posTop > config.height - h) { + dom.translate(this.textarea, 0, 0); + return; + } + var w = 1; + var maxTop = this.$size.height - h; + if (!composition) { + posTop += this.lineHeight; + } else { + if (composition.useTextareaForIME) { + var val = this.textarea.value; + w = this.characterWidth * (this.session.$getStringScreenWidth(val)[0]); + } else { + posTop += this.lineHeight + 2; + } + } + posLeft -= this.scrollLeft; + if (posLeft > this.$size.scrollerWidth - w) + posLeft = this.$size.scrollerWidth - w; + posLeft += this.gutterWidth + this.margin.left; + dom.setStyle(style, "height", h + "px"); + dom.setStyle(style, "width", w + "px"); + dom.translate(this.textarea, Math.min(posLeft, this.$size.scrollerWidth - w), Math.min(posTop, maxTop)); + }; + VirtualRenderer.prototype.getFirstVisibleRow = function() { + return this.layerConfig.firstRow; + }; + VirtualRenderer.prototype.getFirstFullyVisibleRow = function() { + return this.layerConfig.firstRow + (this.layerConfig.offset === 0 ? 0 : 1); + }; + VirtualRenderer.prototype.getLastFullyVisibleRow = function() { + var config = this.layerConfig; + var lastRow = config.lastRow; + var top = this.session.documentToScreenRow(lastRow, 0) * config.lineHeight; + if (top - this.session.getScrollTop() > config.height - config.lineHeight) + return lastRow - 1; + return lastRow; + }; + VirtualRenderer.prototype.getLastVisibleRow = function() { + return this.layerConfig.lastRow; + }; + VirtualRenderer.prototype.setPadding = function(padding) { + this.$padding = padding; + this.$textLayer.setPadding(padding); + this.$cursorLayer.setPadding(padding); + this.$markerFront.setPadding(padding); + this.$markerBack.setPadding(padding); + this.$loop.schedule(this.CHANGE_FULL); + this.$updatePrintMargin(); + }; + VirtualRenderer.prototype.setScrollMargin = function(top, bottom, left, right) { + var sm = this.scrollMargin; + sm.top = top | 0; + sm.bottom = bottom | 0; + sm.right = right | 0; + sm.left = left | 0; + sm.v = sm.top + sm.bottom; + sm.h = sm.left + sm.right; + if (sm.top && this.scrollTop <= 0 && this.session) + this.session.setScrollTop(-sm.top); + this.updateFull(); + }; + VirtualRenderer.prototype.setMargin = function(top, bottom, left, right) { + var sm = this.margin; + sm.top = top | 0; + sm.bottom = bottom | 0; + sm.right = right | 0; + sm.left = left | 0; + sm.v = sm.top + sm.bottom; + sm.h = sm.left + sm.right; + this.$updateCachedSize(true, this.gutterWidth, this.$size.width, this.$size.height); + this.updateFull(); + }; + VirtualRenderer.prototype.getHScrollBarAlwaysVisible = function() { + return this.$hScrollBarAlwaysVisible; + }; + VirtualRenderer.prototype.setHScrollBarAlwaysVisible = function(alwaysVisible) { + this.setOption("hScrollBarAlwaysVisible", alwaysVisible); + }; + VirtualRenderer.prototype.getVScrollBarAlwaysVisible = function() { + return this.$vScrollBarAlwaysVisible; + }; + VirtualRenderer.prototype.setVScrollBarAlwaysVisible = function(alwaysVisible) { + this.setOption("vScrollBarAlwaysVisible", alwaysVisible); + }; + VirtualRenderer.prototype.$updateScrollBarV = function() { + var scrollHeight = this.layerConfig.maxHeight; + var scrollerHeight = this.$size.scrollerHeight; + if (!this.$maxLines && this.$scrollPastEnd) { + scrollHeight -= (scrollerHeight - this.lineHeight) * this.$scrollPastEnd; + if (this.scrollTop > scrollHeight - scrollerHeight) { + scrollHeight = this.scrollTop + scrollerHeight; + this.scrollBarV.scrollTop = null; + } + } + this.scrollBarV.setScrollHeight(scrollHeight + this.scrollMargin.v); + this.scrollBarV.setScrollTop(this.scrollTop + this.scrollMargin.top); + }; + VirtualRenderer.prototype.$updateScrollBarH = function() { + this.scrollBarH.setScrollWidth(this.layerConfig.width + 2 * this.$padding + this.scrollMargin.h); + this.scrollBarH.setScrollLeft(this.scrollLeft + this.scrollMargin.left); + }; + VirtualRenderer.prototype.freeze = function() { + this.$frozen = true; + }; + VirtualRenderer.prototype.unfreeze = function() { + this.$frozen = false; + }; + VirtualRenderer.prototype.$renderChanges = function(changes, force) { + if (this.$changes) { + changes |= this.$changes; + this.$changes = 0; + } + if ((!this.session || !this.container.offsetWidth || this.$frozen) || (!changes && !force)) { + this.$changes |= changes; + return; + } + if (this.$size.$dirty) { + this.$changes |= changes; + return this.onResize(true); + } + if (!this.lineHeight) { + this.$textLayer.checkForSizeChanges(); + } + this._signal("beforeRender", changes); + if (this.session && this.session.$bidiHandler) + this.session.$bidiHandler.updateCharacterWidths(this.$fontMetrics); + var config = this.layerConfig; + if (changes & this.CHANGE_FULL || + changes & this.CHANGE_SIZE || + changes & this.CHANGE_TEXT || + changes & this.CHANGE_LINES || + changes & this.CHANGE_SCROLL || + changes & this.CHANGE_H_SCROLL) { + changes |= this.$computeLayerConfig() | this.$loop.clear(); + if (config.firstRow != this.layerConfig.firstRow && config.firstRowScreen == this.layerConfig.firstRowScreen) { + var st = this.scrollTop + (config.firstRow - Math.max(this.layerConfig.firstRow, 0)) * this.lineHeight; + if (st > 0) { + this.scrollTop = st; + changes = changes | this.CHANGE_SCROLL; + changes |= this.$computeLayerConfig() | this.$loop.clear(); + } + } + config = this.layerConfig; + this.$updateScrollBarV(); + if (changes & this.CHANGE_H_SCROLL) + this.$updateScrollBarH(); + dom.translate(this.content, -this.scrollLeft, -config.offset); + var width = config.width + 2 * this.$padding + "px"; + var height = config.minHeight + "px"; + dom.setStyle(this.content.style, "width", width); + dom.setStyle(this.content.style, "height", height); + } + if (changes & this.CHANGE_H_SCROLL) { + dom.translate(this.content, -this.scrollLeft, -config.offset); + this.scroller.className = this.scrollLeft <= 0 ? "ace_scroller " : "ace_scroller ace_scroll-left "; + if (this.enableKeyboardAccessibility) + this.scroller.className += this.keyboardFocusClassName; + } + if (changes & this.CHANGE_FULL) { + this.$changedLines = null; + this.$textLayer.update(config); + if (this.$showGutter) + this.$gutterLayer.update(config); + if (this.$customScrollbar) { + this.$scrollDecorator.$updateDecorators(config); + } + this.$markerBack.update(config); + this.$markerFront.update(config); + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + this._signal("afterRender", changes); + return; + } + if (changes & this.CHANGE_SCROLL) { + this.$changedLines = null; + if (changes & this.CHANGE_TEXT || changes & this.CHANGE_LINES) + this.$textLayer.update(config); + else + this.$textLayer.scrollLines(config); + if (this.$showGutter) { + if (changes & this.CHANGE_GUTTER || changes & this.CHANGE_LINES) + this.$gutterLayer.update(config); + else + this.$gutterLayer.scrollLines(config); + } + if (this.$customScrollbar) { + this.$scrollDecorator.$updateDecorators(config); + } + this.$markerBack.update(config); + this.$markerFront.update(config); + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + this._signal("afterRender", changes); + return; + } + if (changes & this.CHANGE_TEXT) { + this.$changedLines = null; + this.$textLayer.update(config); + if (this.$showGutter) + this.$gutterLayer.update(config); + if (this.$customScrollbar) { + this.$scrollDecorator.$updateDecorators(config); + } + } else if (changes & this.CHANGE_LINES) { + if (this.$updateLines() || (changes & this.CHANGE_GUTTER) && this.$showGutter) + this.$gutterLayer.update(config); + if (this.$customScrollbar) { + this.$scrollDecorator.$updateDecorators(config); + } + } else if (changes & this.CHANGE_TEXT || changes & this.CHANGE_GUTTER) { + if (this.$showGutter) + this.$gutterLayer.update(config); + if (this.$customScrollbar) { + this.$scrollDecorator.$updateDecorators(config); + } + } else if (changes & this.CHANGE_CURSOR) { + if (this.$highlightGutterLine) + this.$gutterLayer.updateLineHighlight(config); + if (this.$customScrollbar) { + this.$scrollDecorator.$updateDecorators(config); + } + } + if (changes & this.CHANGE_CURSOR) { + this.$cursorLayer.update(config); + this.$moveTextAreaToCursor(); + } + if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_FRONT)) { + this.$markerFront.update(config); + } + if (changes & (this.CHANGE_MARKER | this.CHANGE_MARKER_BACK)) { + this.$markerBack.update(config); + } + this._signal("afterRender", changes); + }; + VirtualRenderer.prototype.$autosize = function() { + var height = this.session.getScreenLength() * this.lineHeight; + var maxHeight = this.$maxLines * this.lineHeight; + var desiredHeight = Math.min(maxHeight, Math.max((this.$minLines || 1) * this.lineHeight, height)) + this.scrollMargin.v + (this.$extraHeight || 0); + if (this.$horizScroll) + desiredHeight += this.scrollBarH.getHeight(); + if (this.$maxPixelHeight && desiredHeight > this.$maxPixelHeight) + desiredHeight = this.$maxPixelHeight; + var hideScrollbars = desiredHeight <= 2 * this.lineHeight; + var vScroll = !hideScrollbars && height > maxHeight; + if (desiredHeight != this.desiredHeight || + this.$size.height != this.desiredHeight || vScroll != this.$vScroll) { + if (vScroll != this.$vScroll) { + this.$vScroll = vScroll; + this.scrollBarV.setVisible(vScroll); + } + var w = this.container.clientWidth; + this.container.style.height = desiredHeight + "px"; + this.$updateCachedSize(true, this.$gutterWidth, w, desiredHeight); + this.desiredHeight = desiredHeight; + this._signal("autosize"); + } + }; + VirtualRenderer.prototype.$computeLayerConfig = function() { + var session = this.session; + var size = this.$size; + var hideScrollbars = size.height <= 2 * this.lineHeight; + var screenLines = this.session.getScreenLength(); + var maxHeight = screenLines * this.lineHeight; + var longestLine = this.$getLongestLine(); + var horizScroll = !hideScrollbars && (this.$hScrollBarAlwaysVisible || + size.scrollerWidth - longestLine - 2 * this.$padding < 0); + var hScrollChanged = this.$horizScroll !== horizScroll; + if (hScrollChanged) { + this.$horizScroll = horizScroll; + this.scrollBarH.setVisible(horizScroll); + } + var vScrollBefore = this.$vScroll; // autosize can change vscroll value in which case we need to update longestLine + if (this.$maxLines && this.lineHeight > 1) + this.$autosize(); + var minHeight = size.scrollerHeight + this.lineHeight; + var scrollPastEnd = !this.$maxLines && this.$scrollPastEnd ? + (size.scrollerHeight - this.lineHeight) * this.$scrollPastEnd : + 0; + maxHeight += scrollPastEnd; + var sm = this.scrollMargin; + this.session.setScrollTop(Math.max(-sm.top, Math.min(this.scrollTop, maxHeight - size.scrollerHeight + sm.bottom))); + this.session.setScrollLeft(Math.max(-sm.left, Math.min(this.scrollLeft, longestLine + 2 * this.$padding - size.scrollerWidth + sm.right))); + var vScroll = !hideScrollbars && (this.$vScrollBarAlwaysVisible || + size.scrollerHeight - maxHeight + scrollPastEnd < 0 || this.scrollTop > sm.top); + var vScrollChanged = vScrollBefore !== vScroll; + if (vScrollChanged) { + this.$vScroll = vScroll; + this.scrollBarV.setVisible(vScroll); + } + var offset = this.scrollTop % this.lineHeight; + var lineCount = Math.ceil(minHeight / this.lineHeight) - 1; + var firstRow = Math.max(0, Math.round((this.scrollTop - offset) / this.lineHeight)); + var lastRow = firstRow + lineCount; + var firstRowScreen, firstRowHeight; + var lineHeight = this.lineHeight; + firstRow = session.screenToDocumentRow(firstRow, 0); + var foldLine = session.getFoldLine(firstRow); + if (foldLine) { + firstRow = foldLine.start.row; + } + firstRowScreen = session.documentToScreenRow(firstRow, 0); + firstRowHeight = session.getRowLength(firstRow) * lineHeight; + lastRow = Math.min(session.screenToDocumentRow(lastRow, 0), session.getLength() - 1); + minHeight = size.scrollerHeight + session.getRowLength(lastRow) * lineHeight + + firstRowHeight; + offset = this.scrollTop - firstRowScreen * lineHeight; + var changes = 0; + if (this.layerConfig.width != longestLine || hScrollChanged) + changes = this.CHANGE_H_SCROLL; + if (hScrollChanged || vScrollChanged) { + changes |= this.$updateCachedSize(true, this.gutterWidth, size.width, size.height); + this._signal("scrollbarVisibilityChanged"); + if (vScrollChanged) + longestLine = this.$getLongestLine(); + } + this.layerConfig = { + width: longestLine, + padding: this.$padding, + firstRow: firstRow, + firstRowScreen: firstRowScreen, + lastRow: lastRow, + lineHeight: lineHeight, + characterWidth: this.characterWidth, + minHeight: minHeight, + maxHeight: maxHeight, + offset: offset, + gutterOffset: lineHeight ? Math.max(0, Math.ceil((offset + size.height - size.scrollerHeight) / lineHeight)) : 0, + height: this.$size.scrollerHeight + }; + if (this.session.$bidiHandler) + this.session.$bidiHandler.setContentWidth(longestLine - this.$padding); + return changes; + }; + VirtualRenderer.prototype.$updateLines = function() { + if (!this.$changedLines) + return; + var firstRow = this.$changedLines.firstRow; + var lastRow = this.$changedLines.lastRow; + this.$changedLines = null; + var layerConfig = this.layerConfig; + if (firstRow > layerConfig.lastRow + 1) { + return; + } + if (lastRow < layerConfig.firstRow) { + return; + } + if (lastRow === Infinity) { + if (this.$showGutter) + this.$gutterLayer.update(layerConfig); + this.$textLayer.update(layerConfig); + return; + } + this.$textLayer.updateLines(layerConfig, firstRow, lastRow); + return true; + }; + VirtualRenderer.prototype.$getLongestLine = function() { + var charCount = this.session.getScreenWidth(); + if (this.showInvisibles && !this.session.$useWrapMode) + charCount += 1; + if (this.$textLayer && charCount > this.$textLayer.MAX_LINE_LENGTH) + charCount = this.$textLayer.MAX_LINE_LENGTH + 30; + return Math.max(this.$size.scrollerWidth - 2 * this.$padding, Math.round(charCount * this.characterWidth)); + }; + VirtualRenderer.prototype.updateFrontMarkers = function() { + this.$markerFront.setMarkers(this.session.getMarkers(true)); + this.$loop.schedule(this.CHANGE_MARKER_FRONT); + }; + VirtualRenderer.prototype.updateBackMarkers = function() { + this.$markerBack.setMarkers(this.session.getMarkers()); + this.$loop.schedule(this.CHANGE_MARKER_BACK); + }; + VirtualRenderer.prototype.addGutterDecoration = function(row, className) { + this.$gutterLayer.addGutterDecoration(row, className); + }; + VirtualRenderer.prototype.removeGutterDecoration = function(row, className) { + this.$gutterLayer.removeGutterDecoration(row, className); + }; + VirtualRenderer.prototype.updateBreakpoints = function(rows) { + this._rows = rows; + this.$loop.schedule(this.CHANGE_GUTTER); + }; + VirtualRenderer.prototype.setAnnotations = function(annotations) { + this.$gutterLayer.setAnnotations(annotations); + this.$loop.schedule(this.CHANGE_GUTTER); + }; + VirtualRenderer.prototype.updateCursor = function() { + this.$loop.schedule(this.CHANGE_CURSOR); + }; + VirtualRenderer.prototype.hideCursor = function() { + this.$cursorLayer.hideCursor(); + }; + VirtualRenderer.prototype.showCursor = function() { + this.$cursorLayer.showCursor(); + }; + VirtualRenderer.prototype.scrollSelectionIntoView = function(anchor, lead, offset) { + this.scrollCursorIntoView(anchor, offset); + this.scrollCursorIntoView(lead, offset); + }; + VirtualRenderer.prototype.scrollCursorIntoView = function(cursor, offset, $viewMargin) { + if (this.$size.scrollerHeight === 0) + return; + var pos = this.$cursorLayer.getPixelPosition(cursor); + var newLeft = pos.left; + var newTop = pos.top; + var topMargin = $viewMargin && $viewMargin.top || 0; + var bottomMargin = $viewMargin && $viewMargin.bottom || 0; + if (this.$scrollAnimation) { + this.$stopAnimation = true; + } + var currentTop = this.$scrollAnimation ? this.session.getScrollTop() : this.scrollTop; + if (currentTop + topMargin > newTop) { + if (offset && currentTop + topMargin > newTop + this.lineHeight) + newTop -= offset * this.$size.scrollerHeight; + if (newTop === 0) + newTop = -this.scrollMargin.top; + this.session.setScrollTop(newTop); + } else if (currentTop + this.$size.scrollerHeight - bottomMargin < newTop + this.lineHeight) { + if (offset && currentTop + this.$size.scrollerHeight - bottomMargin < newTop - this.lineHeight) + newTop += offset * this.$size.scrollerHeight; + this.session.setScrollTop(newTop + this.lineHeight + bottomMargin - this.$size.scrollerHeight); + } + var currentLeft = this.scrollLeft; + var twoCharsWidth = 2 * this.layerConfig.characterWidth; + if (newLeft - twoCharsWidth < currentLeft) { + newLeft -= twoCharsWidth; + if (newLeft < this.$padding + twoCharsWidth) { + newLeft = -this.scrollMargin.left; + } + this.session.setScrollLeft(newLeft); + } else { + newLeft += twoCharsWidth; + if (currentLeft + this.$size.scrollerWidth < newLeft + this.characterWidth) { + this.session.setScrollLeft(Math.round(newLeft + this.characterWidth - this.$size.scrollerWidth)); + } else if (currentLeft <= this.$padding && newLeft - currentLeft < this.characterWidth) { + this.session.setScrollLeft(0); + } + } + }; + VirtualRenderer.prototype.getScrollTop = function() { + return this.session.getScrollTop(); + }; + VirtualRenderer.prototype.getScrollLeft = function() { + return this.session.getScrollLeft(); + }; + VirtualRenderer.prototype.getScrollTopRow = function() { + return this.scrollTop / this.lineHeight; + }; + VirtualRenderer.prototype.getScrollBottomRow = function() { + return Math.max(0, Math.floor((this.scrollTop + this.$size.scrollerHeight) / this.lineHeight) - 1); + }; + VirtualRenderer.prototype.scrollToRow = function(row) { + this.session.setScrollTop(row * this.lineHeight); + }; + VirtualRenderer.prototype.alignCursor = function(cursor, alignment) { + if (typeof cursor == "number") + cursor = { + row: cursor, + column: 0 + }; + var pos = this.$cursorLayer.getPixelPosition(cursor); + var h = this.$size.scrollerHeight - this.lineHeight; + var offset = pos.top - h * (alignment || 0); + this.session.setScrollTop(offset); + return offset; + }; + VirtualRenderer.prototype.$calcSteps = function(fromValue, toValue) { + var i = 0; + var l = this.STEPS; + var steps = []; + var func = function(t, x_min, dx) { + return dx * (Math.pow(t - 1, 3) + 1) + x_min; + }; + for (i = 0; i < l; ++i) + steps.push(func(i / this.STEPS, fromValue, toValue - fromValue)); + return steps; + }; + VirtualRenderer.prototype.scrollToLine = function(line, center, animate, callback) { + var pos = this.$cursorLayer.getPixelPosition({ + row: line, + column: 0 + }); + var offset = pos.top; + if (center) + offset -= this.$size.scrollerHeight / 2; + var initialScroll = this.scrollTop; + this.session.setScrollTop(offset); + if (animate !== false) + this.animateScrolling(initialScroll, callback); + }; + VirtualRenderer.prototype.animateScrolling = function(fromValue, callback) { + var toValue = this.scrollTop; + if (!this.$animatedScroll) + return; + var _self = this; + if (fromValue == toValue) + return; + if (this.$scrollAnimation) { + var oldSteps = this.$scrollAnimation.steps; + if (oldSteps.length) { + fromValue = oldSteps[0]; + if (fromValue == toValue) + return; + } + } + var steps = _self.$calcSteps(fromValue, toValue); + this.$scrollAnimation = { + from: fromValue, + to: toValue, + steps: steps + }; + clearInterval(this.$timer); + _self.session.setScrollTop(steps.shift()); + _self.session.$scrollTop = toValue; + + function endAnimation() { + _self.$timer = clearInterval(_self.$timer); + _self.$scrollAnimation = null; + _self.$stopAnimation = false; + callback && callback(); + } + this.$timer = setInterval(function() { + if (_self.$stopAnimation) { + endAnimation(); + return; + } + if (!_self.session) + return clearInterval(_self.$timer); + if (steps.length) { + _self.session.setScrollTop(steps.shift()); + _self.session.$scrollTop = toValue; + } else if (toValue != null) { + _self.session.$scrollTop = -1; + _self.session.setScrollTop(toValue); + toValue = null; + } else { + endAnimation(); + } + }, 10); + }; + VirtualRenderer.prototype.scrollToY = function(scrollTop) { + if (this.scrollTop !== scrollTop) { + this.$loop.schedule(this.CHANGE_SCROLL); + this.scrollTop = scrollTop; + } + }; + VirtualRenderer.prototype.scrollToX = function(scrollLeft) { + if (this.scrollLeft !== scrollLeft) + this.scrollLeft = scrollLeft; + this.$loop.schedule(this.CHANGE_H_SCROLL); + }; + VirtualRenderer.prototype.scrollTo = function(x, y) { + this.session.setScrollTop(y); + this.session.setScrollLeft(x); + }; + VirtualRenderer.prototype.scrollBy = function(deltaX, deltaY) { + deltaY && this.session.setScrollTop(this.session.getScrollTop() + deltaY); + deltaX && this.session.setScrollLeft(this.session.getScrollLeft() + deltaX); + }; + VirtualRenderer.prototype.isScrollableBy = function(deltaX, deltaY) { + if (deltaY < 0 && this.session.getScrollTop() >= 1 - this.scrollMargin.top) + return true; + if (deltaY > 0 && this.session.getScrollTop() + this.$size.scrollerHeight - + this.layerConfig.maxHeight < -1 + this.scrollMargin.bottom) + return true; + if (deltaX < 0 && this.session.getScrollLeft() >= 1 - this.scrollMargin.left) + return true; + if (deltaX > 0 && this.session.getScrollLeft() + this.$size.scrollerWidth - + this.layerConfig.width < -1 + this.scrollMargin.right) + return true; + }; + VirtualRenderer.prototype.pixelToScreenCoordinates = function(x, y) { + var canvasPos; + if (this.$hasCssTransforms) { + canvasPos = { + top: 0, + left: 0 + }; + var p = this.$fontMetrics.transformCoordinates([x, y]); + x = p[1] - this.gutterWidth - this.margin.left; + y = p[0]; + } else { + canvasPos = this.scroller.getBoundingClientRect(); + } + var offsetX = x + this.scrollLeft - canvasPos.left - this.$padding; + var offset = offsetX / this.characterWidth; + var row = Math.floor((y + this.scrollTop - canvasPos.top) / this.lineHeight); + var col = this.$blockCursor ? Math.floor(offset) : Math.round(offset); + return { + row: row, + column: col, + side: offset - col > 0 ? 1 : -1, + offsetX: offsetX + }; + }; + VirtualRenderer.prototype.screenToTextCoordinates = function(x, y) { + var canvasPos; + if (this.$hasCssTransforms) { + canvasPos = { + top: 0, + left: 0 + }; + var p = this.$fontMetrics.transformCoordinates([x, y]); + x = p[1] - this.gutterWidth - this.margin.left; + y = p[0]; + } else { + canvasPos = this.scroller.getBoundingClientRect(); + } + var offsetX = x + this.scrollLeft - canvasPos.left - this.$padding; + var offset = offsetX / this.characterWidth; + var col = this.$blockCursor ? Math.floor(offset) : Math.round(offset); + var row = Math.floor((y + this.scrollTop - canvasPos.top) / this.lineHeight); + return this.session.screenToDocumentPosition(row, Math.max(col, 0), offsetX); + }; + VirtualRenderer.prototype.textToScreenCoordinates = function(row, column) { + var canvasPos = this.scroller.getBoundingClientRect(); + var pos = this.session.documentToScreenPosition(row, column); + var x = this.$padding + (this.session.$bidiHandler.isBidiRow(pos.row, row) ? + this.session.$bidiHandler.getPosLeft(pos.column) : + Math.round(pos.column * this.characterWidth)); + var y = pos.row * this.lineHeight; + return { + pageX: canvasPos.left + x - this.scrollLeft, + pageY: canvasPos.top + y - this.scrollTop + }; + }; + VirtualRenderer.prototype.visualizeFocus = function() { + dom.addCssClass(this.container, "ace_focus"); + }; + VirtualRenderer.prototype.visualizeBlur = function() { + dom.removeCssClass(this.container, "ace_focus"); + }; + VirtualRenderer.prototype.showComposition = function(composition) { + this.$composition = composition; + if (!composition.cssText) { + composition.cssText = this.textarea.style.cssText; + } + if (composition.useTextareaForIME == undefined) + composition.useTextareaForIME = this.$useTextareaForIME; + if (this.$useTextareaForIME) { + dom.addCssClass(this.textarea, "ace_composition"); + this.textarea.style.cssText = ""; + this.$moveTextAreaToCursor(); + this.$cursorLayer.element.style.display = "none"; + } else { + composition.markerId = this.session.addMarker(composition.markerRange, "ace_composition_marker", "text"); + } + }; + VirtualRenderer.prototype.setCompositionText = function(text) { + var cursor = this.session.selection.cursor; + this.addToken(text, "composition_placeholder", cursor.row, cursor.column); + this.$moveTextAreaToCursor(); + }; + VirtualRenderer.prototype.hideComposition = function() { + if (!this.$composition) + return; + if (this.$composition.markerId) + this.session.removeMarker(this.$composition.markerId); + dom.removeCssClass(this.textarea, "ace_composition"); + this.textarea.style.cssText = this.$composition.cssText; + var cursor = this.session.selection.cursor; + this.removeExtraToken(cursor.row, cursor.column); + this.$composition = null; + this.$cursorLayer.element.style.display = ""; + }; + VirtualRenderer.prototype.setGhostText = function(text, position) { + var cursor = this.session.selection.cursor; + var insertPosition = position || { + row: cursor.row, + column: cursor.column + }; + this.removeGhostText(); + var textChunks = this.$calculateWrappedTextChunks(text, insertPosition); + this.addToken(textChunks[0].text, "ghost_text", insertPosition.row, insertPosition.column); + this.$ghostText = { + text: text, + position: { + row: insertPosition.row, + column: insertPosition.column + } + }; + var widgetDiv = dom.createElement("div"); + if (textChunks.length > 1) { + var hiddenTokens = this.hideTokensAfterPosition(insertPosition.row, insertPosition.column); + var lastLineDiv; + textChunks.slice(1).forEach(function(el) { + var chunkDiv = dom.createElement("div"); + var chunkSpan = dom.createElement("span"); + chunkSpan.className = "ace_ghost_text"; + if (el.wrapped) + chunkDiv.className = "ghost_text_line_wrapped"; + if (el.text.length === 0) + el.text = " "; + chunkSpan.appendChild(dom.createTextNode(el.text)); + chunkDiv.appendChild(chunkSpan); + widgetDiv.appendChild(chunkDiv); + lastLineDiv = chunkDiv; + }); + hiddenTokens.forEach(function(token) { + var element = dom.createElement("span"); + if (!isTextToken(token.type)) + element.className = "ace_" + token.type.replace(/\./g, " ace_"); + element.appendChild(dom.createTextNode(token.value)); + lastLineDiv.appendChild(element); + }); + this.$ghostTextWidget = { + el: widgetDiv, + row: insertPosition.row, + column: insertPosition.column, + className: "ace_ghost_text_container" + }; + this.session.widgetManager.addLineWidget(this.$ghostTextWidget); + var pixelPosition = this.$cursorLayer.getPixelPosition(insertPosition, true); + var el = this.container; + var height = el.getBoundingClientRect().height; + var ghostTextHeight = textChunks.length * this.lineHeight; + var fitsY = ghostTextHeight < (height - pixelPosition.top); + if (fitsY) + return; + if (ghostTextHeight < height) { + this.scrollBy(0, (textChunks.length - 1) * this.lineHeight); + } else { + this.scrollToRow(insertPosition.row); + } + } + }; + VirtualRenderer.prototype.$calculateWrappedTextChunks = function(text, position) { + var availableWidth = this.$size.scrollerWidth - this.$padding * 2; + var limit = Math.floor(availableWidth / this.characterWidth) - 2; + limit = limit <= 0 ? 60 : limit; // this is a hack to prevent the editor from crashing when the window is too small + var textLines = text.split(/\r?\n/); + var textChunks = []; + for (var i = 0; i < textLines.length; i++) { + var displayTokens = this.session.$getDisplayTokens(textLines[i], position.column); + var wrapSplits = this.session.$computeWrapSplits(displayTokens, limit, this.session.$tabSize); + if (wrapSplits.length > 0) { + var start = 0; + wrapSplits.push(textLines[i].length); + for (var j = 0; j < wrapSplits.length; j++) { + var textSlice = textLines[i].slice(start, wrapSplits[j]); + textChunks.push({ + text: textSlice, + wrapped: true + }); + start = wrapSplits[j]; + } + } else { + textChunks.push({ + text: textLines[i], + wrapped: false + }); + } + } + return textChunks; + }; + VirtualRenderer.prototype.removeGhostText = function() { + if (!this.$ghostText) + return; + var position = this.$ghostText.position; + this.removeExtraToken(position.row, position.column); + if (this.$ghostTextWidget) { + this.session.widgetManager.removeLineWidget(this.$ghostTextWidget); + this.$ghostTextWidget = null; + } + this.$ghostText = null; + }; + VirtualRenderer.prototype.addToken = function(text, type, row, column) { + var session = this.session; + session.bgTokenizer.lines[row] = null; + var newToken = { + type: type, + value: text + }; + var tokens = session.getTokens(row); + if (column == null || !tokens.length) { + tokens.push(newToken); + } else { + var l = 0; + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + l += token.value.length; + if (column <= l) { + var diff = token.value.length - (l - column); + var before = token.value.slice(0, diff); + var after = token.value.slice(diff); + tokens.splice(i, 1, { + type: token.type, + value: before + }, newToken, { + type: token.type, + value: after + }); + break; + } + } + } + this.updateLines(row, row); + }; + VirtualRenderer.prototype.hideTokensAfterPosition = function(row, column) { + var tokens = this.session.getTokens(row); + var l = 0; + var hasPassedCursor = false; + var hiddenTokens = []; + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + l += token.value.length; + if (token.type === "ghost_text") + continue; + if (hasPassedCursor) { + hiddenTokens.push({ + type: token.type, + value: token.value + }); + token.type = "hidden_token"; + continue; + } + if (l === column) { + hasPassedCursor = true; + } + } + this.updateLines(row, row); + return hiddenTokens; + }; + VirtualRenderer.prototype.removeExtraToken = function(row, column) { + this.session.bgTokenizer.lines[row] = null; + this.updateLines(row, row); + }; + VirtualRenderer.prototype.setTheme = function(theme, cb) { + var _self = this; + this.$themeId = theme; + _self._dispatchEvent('themeChange', { + theme: theme + }); + if (!theme || typeof theme == "string") { + var moduleName = theme || this.$options.theme.initialValue; + config.loadModule(["theme", moduleName], afterLoad); + } else { + afterLoad(theme); + } + + function afterLoad(module) { + if (_self.$themeId != theme) + return cb && cb(); + if (!module || !module.cssClass) + throw new Error("couldn't load module " + theme + " or it didn't call define"); + if (module.$id) + _self.$themeId = module.$id; + dom.importCssString(module.cssText, module.cssClass, _self.container); + if (_self.theme) + dom.removeCssClass(_self.container, _self.theme.cssClass); + var padding = "padding" in module ? module.padding : + "padding" in (_self.theme || {}) ? 4 : _self.$padding; + if (_self.$padding && padding != _self.$padding) + _self.setPadding(padding); + _self.$theme = module.cssClass; + _self.theme = module; + dom.addCssClass(_self.container, module.cssClass); + dom.setCssClass(_self.container, "ace_dark", module.isDark); + if (_self.$size) { + _self.$size.width = 0; + _self.$updateSizeAsync(); + } + _self._dispatchEvent('themeLoaded', { + theme: module + }); + cb && cb(); + if (useragent.isSafari && _self.scroller) { + _self.scroller.style.background = "red"; + _self.scroller.style.background = ""; + } + } + }; + VirtualRenderer.prototype.getTheme = function() { + return this.$themeId; + }; + VirtualRenderer.prototype.setStyle = function(style, include) { + dom.setCssClass(this.container, style, include !== false); + }; + VirtualRenderer.prototype.unsetStyle = function(style) { + dom.removeCssClass(this.container, style); + }; + VirtualRenderer.prototype.setCursorStyle = function(style) { + dom.setStyle(this.scroller.style, "cursor", style); + }; + VirtualRenderer.prototype.setMouseCursor = function(cursorStyle) { + dom.setStyle(this.scroller.style, "cursor", cursorStyle); + }; + VirtualRenderer.prototype.attachToShadowRoot = function() { + dom.importCssString(editorCss, "ace_editor.css", this.container); + }; + VirtualRenderer.prototype.destroy = function() { + this.freeze(); + this.$fontMetrics.destroy(); + this.$cursorLayer.destroy(); + this.removeAllListeners(); + this.container.textContent = ""; + this.setOption("useResizeObserver", false); + }; + VirtualRenderer.prototype.$updateCustomScrollbar = function(val) { + var _self = this; + this.$horizScroll = this.$vScroll = null; + this.scrollBarV.element.remove(); + this.scrollBarH.element.remove(); + if (this.$scrollDecorator) { + delete this.$scrollDecorator; + } + if (val === true) { + this.scrollBarV = new VScrollBarCustom(this.container, this); + this.scrollBarH = new HScrollBarCustom(this.container, this); + this.scrollBarV.setHeight(this.$size.scrollerHeight); + this.scrollBarH.setWidth(this.$size.scrollerWidth); + this.scrollBarV.addEventListener("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollTop(e.data - _self.scrollMargin.top); + }); + this.scrollBarH.addEventListener("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollLeft(e.data - _self.scrollMargin.left); + }); + this.$scrollDecorator = new Decorator(this.scrollBarV, this); + this.$scrollDecorator.$updateDecorators(); + } else { + this.scrollBarV = new VScrollBar(this.container, this); + this.scrollBarH = new HScrollBar(this.container, this); + this.scrollBarV.addEventListener("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollTop(e.data - _self.scrollMargin.top); + }); + this.scrollBarH.addEventListener("scroll", function(e) { + if (!_self.$scrollAnimation) + _self.session.setScrollLeft(e.data - _self.scrollMargin.left); + }); + } + }; + VirtualRenderer.prototype.$addResizeObserver = function() { + if (!window.ResizeObserver || this.$resizeObserver) + return; + var self = this; + this.$resizeTimer = lang.delayedCall(function() { + if (!self.destroyed) + self.onResize(); + }, 50); + this.$resizeObserver = new window.ResizeObserver(function(e) { + var w = e[0].contentRect.width; + var h = e[0].contentRect.height; + if (Math.abs(self.$size.width - w) > 1 || + Math.abs(self.$size.height - h) > 1) { + self.$resizeTimer.delay(); + } else { + self.$resizeTimer.cancel(); + } + }); + this.$resizeObserver.observe(this.container); + }; + return VirtualRenderer; + }()); + VirtualRenderer.prototype.CHANGE_CURSOR = 1; + VirtualRenderer.prototype.CHANGE_MARKER = 2; + VirtualRenderer.prototype.CHANGE_GUTTER = 4; + VirtualRenderer.prototype.CHANGE_SCROLL = 8; + VirtualRenderer.prototype.CHANGE_LINES = 16; + VirtualRenderer.prototype.CHANGE_TEXT = 32; + VirtualRenderer.prototype.CHANGE_SIZE = 64; + VirtualRenderer.prototype.CHANGE_MARKER_BACK = 128; + VirtualRenderer.prototype.CHANGE_MARKER_FRONT = 256; + VirtualRenderer.prototype.CHANGE_FULL = 512; + VirtualRenderer.prototype.CHANGE_H_SCROLL = 1024; + VirtualRenderer.prototype.$changes = 0; + VirtualRenderer.prototype.$padding = null; + VirtualRenderer.prototype.$frozen = false; + VirtualRenderer.prototype.STEPS = 8; + oop.implement(VirtualRenderer.prototype, EventEmitter); + config.defineOptions(VirtualRenderer.prototype, "renderer", { + useResizeObserver: { + set: function(value) { + if (!value && this.$resizeObserver) { + this.$resizeObserver.disconnect(); + this.$resizeTimer.cancel(); + this.$resizeTimer = this.$resizeObserver = null; + } else if (value && !this.$resizeObserver) { + this.$addResizeObserver(); + } + } + }, + animatedScroll: { + initialValue: false + }, + showInvisibles: { + set: function(value) { + if (this.$textLayer.setShowInvisibles(value)) + this.$loop.schedule(this.CHANGE_TEXT); + }, + initialValue: false + }, + showPrintMargin: { + set: function() { + this.$updatePrintMargin(); + }, + initialValue: true + }, + printMarginColumn: { + set: function() { + this.$updatePrintMargin(); + }, + initialValue: 80 + }, + printMargin: { + set: function(val) { + if (typeof val == "number") + this.$printMarginColumn = val; + this.$showPrintMargin = !!val; + this.$updatePrintMargin(); + }, + get: function() { + return this.$showPrintMargin && this.$printMarginColumn; + } + }, + showGutter: { + set: function(show) { + this.$gutter.style.display = show ? "block" : "none"; + this.$loop.schedule(this.CHANGE_FULL); + this.onGutterResize(); + }, + initialValue: true + }, + useSvgGutterIcons: { + set: function(value) { + this.$gutterLayer.$useSvgGutterIcons = value; + }, + initialValue: false + }, + showFoldedAnnotations: { + set: function(value) { + this.$gutterLayer.$showFoldedAnnotations = value; + }, + initialValue: false + }, + fadeFoldWidgets: { + set: function(show) { + dom.setCssClass(this.$gutter, "ace_fade-fold-widgets", show); + }, + initialValue: false + }, + showFoldWidgets: { + set: function(show) { + this.$gutterLayer.setShowFoldWidgets(show); + this.$loop.schedule(this.CHANGE_GUTTER); + }, + initialValue: true + }, + displayIndentGuides: { + set: function(show) { + if (this.$textLayer.setDisplayIndentGuides(show)) + this.$loop.schedule(this.CHANGE_TEXT); + }, + initialValue: true + }, + highlightIndentGuides: { + set: function(show) { + if (this.$textLayer.setHighlightIndentGuides(show) == true) { + this.$textLayer.$highlightIndentGuide(); + } else { + this.$textLayer.$clearActiveIndentGuide(this.$textLayer.$lines.cells); + } + }, + initialValue: true + }, + highlightGutterLine: { + set: function(shouldHighlight) { + this.$gutterLayer.setHighlightGutterLine(shouldHighlight); + this.$loop.schedule(this.CHANGE_GUTTER); + }, + initialValue: true + }, + hScrollBarAlwaysVisible: { + set: function(val) { + if (!this.$hScrollBarAlwaysVisible || !this.$horizScroll) + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: false + }, + vScrollBarAlwaysVisible: { + set: function(val) { + if (!this.$vScrollBarAlwaysVisible || !this.$vScroll) + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: false + }, + fontSize: { + set: function(size) { + if (typeof size == "number") + size = size + "px"; + this.container.style.fontSize = size; + this.updateFontSize(); + }, + initialValue: 12 + }, + fontFamily: { + set: function(name) { + this.container.style.fontFamily = name; + this.updateFontSize(); + } + }, + maxLines: { + set: function(val) { + this.updateFull(); + } + }, + minLines: { + set: function(val) { + if (!(this.$minLines < 0x1ffffffffffff)) + this.$minLines = 0; + this.updateFull(); + } + }, + maxPixelHeight: { + set: function(val) { + this.updateFull(); + }, + initialValue: 0 + }, + scrollPastEnd: { + set: function(val) { + val = +val || 0; + if (this.$scrollPastEnd == val) + return; + this.$scrollPastEnd = val; + this.$loop.schedule(this.CHANGE_SCROLL); + }, + initialValue: 0, + handlesSet: true + }, + fixedWidthGutter: { + set: function(val) { + this.$gutterLayer.$fixedWidth = !!val; + this.$loop.schedule(this.CHANGE_GUTTER); + } + }, + customScrollbar: { + set: function(val) { + this.$updateCustomScrollbar(val); + }, + initialValue: false + }, + theme: { + set: function(val) { + this.setTheme(val); + }, + get: function() { + return this.$themeId || this.theme; + }, + initialValue: "./theme/textmate", + handlesSet: true + }, + hasCssTransforms: {}, + useTextareaForIME: { + initialValue: !useragent.isMobile && !useragent.isIE + } + }); + exports.VirtualRenderer = VirtualRenderer; + +}); + +define("ace/worker/worker_client", ["require", "exports", "module", "ace/lib/oop", "ace/lib/net", "ace/lib/event_emitter", "ace/config"], function(require, exports, module) { + "use strict"; + + var oop = require("../lib/oop"); + var net = require("../lib/net"); + var EventEmitter = require("../lib/event_emitter").EventEmitter; + var config = require("../config"); + + function $workerBlob(workerUrl) { + var script = "importScripts('" + net.qualifyURL(workerUrl) + "');"; + try { + return new Blob([script], { + "type": "application/javascript" + }); + } catch (e) { // Backwards-compatibility + var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder; + var blobBuilder = new BlobBuilder(); + blobBuilder.append(script); + return blobBuilder.getBlob("application/javascript"); + } + } + + function createWorker(workerUrl) { + if (typeof Worker == "undefined") + return { + postMessage: function() {}, + terminate: function() {} + }; + if (config.get("loadWorkerFromBlob")) { + var blob = $workerBlob(workerUrl); + var URL = window.URL || window.webkitURL; + var blobURL = URL.createObjectURL(blob); + return new Worker(blobURL); + } + return new Worker(workerUrl); + } + + var WorkerClient = function(worker) { + if (!worker.postMessage) + worker = this.$createWorkerFromOldConfig.apply(this, arguments); + + this.$worker = worker; + this.$sendDeltaQueue = this.$sendDeltaQueue.bind(this); + this.changeListener = this.changeListener.bind(this); + this.onMessage = this.onMessage.bind(this); + + this.callbackId = 1; + this.callbacks = {}; + + this.$worker.onmessage = this.onMessage; + }; + + (function() { + + oop.implement(this, EventEmitter); + + this.$createWorkerFromOldConfig = function(topLevelNamespaces, mod, classname, workerUrl, importScripts) { + if (require.nameToUrl && !require.toUrl) + require.toUrl = require.nameToUrl; + + if (config.get("packaged") || !require.toUrl) { + workerUrl = workerUrl || config.moduleUrl(mod, "worker"); + } else { + var normalizePath = this.$normalizePath; + workerUrl = workerUrl || normalizePath(require.toUrl("ace/worker/worker.js", null, "_")); + + var tlns = {}; + topLevelNamespaces.forEach(function(ns) { + tlns[ns] = normalizePath(require.toUrl(ns, null, "_").replace(/(\.js)?(\?.*)?$/, "")); + }); + } + + this.$worker = createWorker(workerUrl); + if (importScripts) { + this.send("importScripts", importScripts); + } + this.$worker.postMessage({ + init: true, + tlns: tlns, + module: mod, + classname: classname + }); + return this.$worker; + }; + + this.onMessage = function(e) { + var msg = e.data; + switch (msg.type) { + case "event": + this._signal(msg.name, { + data: msg.data + }); + break; + case "call": + var callback = this.callbacks[msg.id]; + if (callback) { + callback(msg.data); + delete this.callbacks[msg.id]; + } + break; + case "error": + this.reportError(msg.data); + break; + case "log": + window.console && console.log && console.log.apply(console, msg.data); + break; + } + }; + + this.reportError = function(err) { + window.console && console.error && console.error(err); + }; + + this.$normalizePath = function(path) { + return net.qualifyURL(path); + }; + + this.terminate = function() { + this._signal("terminate", {}); + this.deltaQueue = null; + this.$worker.terminate(); + this.$worker.onerror = function(e) { + e.preventDefault(); + }; + this.$worker = null; + if (this.$doc) + this.$doc.off("change", this.changeListener); + this.$doc = null; + }; + + this.send = function(cmd, args) { + this.$worker.postMessage({ + command: cmd, + args: args + }); + }; + + this.call = function(cmd, args, callback) { + if (callback) { + var id = this.callbackId++; + this.callbacks[id] = callback; + args.push(id); + } + this.send(cmd, args); + }; + + this.emit = function(event, data) { + try { + if (data.data && data.data.err) + data.data.err = { + message: data.data.err.message, + stack: data.data.err.stack, + code: data.data.err.code + }; + this.$worker && this.$worker.postMessage({ + event: event, + data: { + data: data.data + } + }); + } catch (ex) { + console.error(ex.stack); + } + }; + + this.attachToDocument = function(doc) { + if (this.$doc) + this.terminate(); + + this.$doc = doc; + this.call("setValue", [doc.getValue()]); + doc.on("change", this.changeListener, true); + }; + + this.changeListener = function(delta) { + if (!this.deltaQueue) { + this.deltaQueue = []; + setTimeout(this.$sendDeltaQueue, 0); + } + if (delta.action == "insert") + this.deltaQueue.push(delta.start, delta.lines); + else + this.deltaQueue.push(delta.start, delta.end); + }; + + this.$sendDeltaQueue = function() { + var q = this.deltaQueue; + if (!q) return; + this.deltaQueue = null; + if (q.length > 50 && q.length > this.$doc.getLength() >> 1) { + this.call("setValue", [this.$doc.getValue()]); + } else + this.emit("change", { + data: q + }); + }; + + }).call(WorkerClient.prototype); + + + var UIWorkerClient = function(topLevelNamespaces, mod, classname) { + var main = null; + var emitSync = false; + var sender = Object.create(EventEmitter); + + var messageBuffer = []; + var workerClient = new WorkerClient({ + messageBuffer: messageBuffer, + terminate: function() {}, + postMessage: function(e) { + messageBuffer.push(e); + if (!main) return; + if (emitSync) + setTimeout(processNext); + else + processNext(); + } + }); + + workerClient.setEmitSync = function(val) { + emitSync = val; + }; + + var processNext = function() { + var msg = messageBuffer.shift(); + if (msg.command) + main[msg.command].apply(main, msg.args); + else if (msg.event) + sender._signal(msg.event, msg.data); + }; + + sender.postMessage = function(msg) { + workerClient.onMessage({ + data: msg + }); + }; + sender.callback = function(data, callbackId) { + this.postMessage({ + type: "call", + id: callbackId, + data: data + }); + }; + sender.emit = function(name, data) { + this.postMessage({ + type: "event", + name: name, + data: data + }); + }; + + config.loadModule(["worker", mod], function(Main) { + main = new Main[classname](sender); + while (messageBuffer.length) + processNext(); + }); + + return workerClient; + }; + + exports.UIWorkerClient = UIWorkerClient; + exports.WorkerClient = WorkerClient; + exports.createWorker = createWorker; + + +}); + +define("ace/placeholder", ["require", "exports", "module", "ace/range", "ace/lib/event_emitter", "ace/lib/oop"], function(require, exports, module) { + "use strict"; + var Range = require("./range").Range; + var EventEmitter = require("./lib/event_emitter").EventEmitter; + var oop = require("./lib/oop"); + var PlaceHolder = /** @class */ (function() { + function PlaceHolder(session, length, pos, others, mainClass, othersClass) { + var _self = this; + this.length = length; + this.session = session; + this.doc = session.getDocument(); + this.mainClass = mainClass; + this.othersClass = othersClass; + this.$onUpdate = this.onUpdate.bind(this); + this.doc.on("change", this.$onUpdate, true); + this.$others = others; + this.$onCursorChange = function() { + setTimeout(function() { + _self.onCursorChange(); + }); + }; + this.$pos = pos; + var undoStack = session.getUndoManager().$undoStack || session.getUndoManager()["$undostack"] || { + length: -1 + }; + this.$undoStackDepth = undoStack.length; + this.setup(); + session.selection.on("changeCursor", this.$onCursorChange); + } + PlaceHolder.prototype.setup = function() { + var _self = this; + var doc = this.doc; + var session = this.session; + this.selectionBefore = session.selection.toJSON(); + if (session.selection.inMultiSelectMode) + session.selection.toSingleRange(); + this.pos = doc.createAnchor(this.$pos.row, this.$pos.column); + var pos = this.pos; + pos.$insertRight = true; + pos.detach(); + pos.markerId = session.addMarker(new Range(pos.row, pos.column, pos.row, pos.column + this.length), this.mainClass, null, false); + this.others = []; + this.$others.forEach(function(other) { + var anchor = doc.createAnchor(other.row, other.column); + anchor.$insertRight = true; + anchor.detach(); + _self.others.push(anchor); + }); + session.setUndoSelect(false); + }; + PlaceHolder.prototype.showOtherMarkers = function() { + if (this.othersActive) + return; + var session = this.session; + var _self = this; + this.othersActive = true; + this.others.forEach(function(anchor) { + anchor.markerId = session.addMarker(new Range(anchor.row, anchor.column, anchor.row, anchor.column + _self.length), _self.othersClass, null, false); + }); + }; + PlaceHolder.prototype.hideOtherMarkers = function() { + if (!this.othersActive) + return; + this.othersActive = false; + for (var i = 0; i < this.others.length; i++) { + this.session.removeMarker(this.others[i].markerId); + } + }; + PlaceHolder.prototype.onUpdate = function(delta) { + if (this.$updating) + return this.updateAnchors(delta); + var range = delta; + if (range.start.row !== range.end.row) + return; + if (range.start.row !== this.pos.row) + return; + this.$updating = true; + var lengthDiff = delta.action === "insert" ? range.end.column - range.start.column : range.start.column - range.end.column; + var inMainRange = range.start.column >= this.pos.column && range.start.column <= this.pos.column + this.length + 1; + var distanceFromStart = range.start.column - this.pos.column; + this.updateAnchors(delta); + if (inMainRange) + this.length += lengthDiff; + if (inMainRange && !this.session.$fromUndo) { + if (delta.action === 'insert') { + for (var i = this.others.length - 1; i >= 0; i--) { + var otherPos = this.others[i]; + var newPos = { + row: otherPos.row, + column: otherPos.column + distanceFromStart + }; + this.doc.insertMergedLines(newPos, delta.lines); + } + } else if (delta.action === 'remove') { + for (var i = this.others.length - 1; i >= 0; i--) { + var otherPos = this.others[i]; + var newPos = { + row: otherPos.row, + column: otherPos.column + distanceFromStart + }; + this.doc.remove(new Range(newPos.row, newPos.column, newPos.row, newPos.column - lengthDiff)); + } + } + } + this.$updating = false; + this.updateMarkers(); + }; + PlaceHolder.prototype.updateAnchors = function(delta) { + this.pos.onChange(delta); + for (var i = this.others.length; i--;) + this.others[i].onChange(delta); + this.updateMarkers(); + }; + PlaceHolder.prototype.updateMarkers = function() { + if (this.$updating) + return; + var _self = this; + var session = this.session; + var updateMarker = function(pos, className) { + session.removeMarker(pos.markerId); + pos.markerId = session.addMarker(new Range(pos.row, pos.column, pos.row, pos.column + _self.length), className, null, false); + }; + updateMarker(this.pos, this.mainClass); + for (var i = this.others.length; i--;) + updateMarker(this.others[i], this.othersClass); + }; + PlaceHolder.prototype.onCursorChange = function(event) { + if (this.$updating || !this.session) + return; + var pos = this.session.selection.getCursor(); + if (pos.row === this.pos.row && pos.column >= this.pos.column && pos.column <= this.pos.column + this.length) { + this.showOtherMarkers(); + this._emit("cursorEnter", event); + } else { + this.hideOtherMarkers(); + this._emit("cursorLeave", event); + } + }; + PlaceHolder.prototype.detach = function() { + this.session.removeMarker(this.pos && this.pos.markerId); + this.hideOtherMarkers(); + this.doc.off("change", this.$onUpdate); + this.session.selection.off("changeCursor", this.$onCursorChange); + this.session.setUndoSelect(true); + this.session = null; + }; + PlaceHolder.prototype.cancel = function() { + if (this.$undoStackDepth === -1) + return; + var undoManager = this.session.getUndoManager(); + var undosRequired = (undoManager.$undoStack || undoManager["$undostack"]).length - this.$undoStackDepth; + for (var i = 0; i < undosRequired; i++) { + undoManager.undo(this.session, true); + } + if (this.selectionBefore) + this.session.selection.fromJSON(this.selectionBefore); + }; + return PlaceHolder; + }()); + oop.implement(PlaceHolder.prototype, EventEmitter); + exports.PlaceHolder = PlaceHolder; + +}); + +define("ace/mouse/multi_select_handler", ["require", "exports", "module", "ace/lib/event", "ace/lib/useragent"], function(require, exports, module) { + var event = require("../lib/event"); + var useragent = require("../lib/useragent"); + + function isSamePoint(p1, p2) { + return p1.row == p2.row && p1.column == p2.column; + } + + function onMouseDown(e) { + var ev = e.domEvent; + var alt = ev.altKey; + var shift = ev.shiftKey; + var ctrl = ev.ctrlKey; + var accel = e.getAccelKey(); + var button = e.getButton(); + if (ctrl && useragent.isMac) + button = ev.button; + if (e.editor.inMultiSelectMode && button == 2) { + e.editor.textInput.onContextMenu(e.domEvent); + return; + } + if (!ctrl && !alt && !accel) { + if (button === 0 && e.editor.inMultiSelectMode) + e.editor.exitMultiSelectMode(); + return; + } + if (button !== 0) + return; + var editor = e.editor; + var selection = editor.selection; + var isMultiSelect = editor.inMultiSelectMode; + var pos = e.getDocumentPosition(); + var cursor = selection.getCursor(); + var inSelection = e.inSelection() || (selection.isEmpty() && isSamePoint(pos, cursor)); + var mouseX = e.x, + mouseY = e.y; + var onMouseSelection = function(e) { + mouseX = e.clientX; + mouseY = e.clientY; + }; + var session = editor.session; + var screenAnchor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var screenCursor = screenAnchor; + var selectionMode; + if (editor.$mouseHandler.$enableJumpToDef) { + if (ctrl && alt || accel && alt) + selectionMode = shift ? "block" : "add"; + else if (alt && editor.$blockSelectEnabled) + selectionMode = "block"; + } else { + if (accel && !alt) { + selectionMode = "add"; + if (!isMultiSelect && shift) + return; + } else if (alt && editor.$blockSelectEnabled) { + selectionMode = "block"; + } + } + if (selectionMode && useragent.isMac && ev.ctrlKey) { + editor.$mouseHandler.cancelContextMenu(); + } + if (selectionMode == "add") { + if (!isMultiSelect && inSelection) + return; // dragging + if (!isMultiSelect) { + var range = selection.toOrientedRange(); + editor.addSelectionMarker(range); + } + var oldRange = selection.rangeList.rangeAtPoint(pos); + editor.inVirtualSelectionMode = true; + if (shift) { + oldRange = null; + range = selection.ranges[0] || range; + editor.removeSelectionMarker(range); + } + editor.once("mouseup", function() { + var tmpSel = selection.toOrientedRange(); + if (oldRange && tmpSel.isEmpty() && isSamePoint(oldRange.cursor, tmpSel.cursor)) + selection.substractPoint(tmpSel.cursor); + else { + if (shift) { + selection.substractPoint(range.cursor); + } else if (range) { + editor.removeSelectionMarker(range); + selection.addRange(range); + } + selection.addRange(tmpSel); + } + editor.inVirtualSelectionMode = false; + }); + } else if (selectionMode == "block") { + e.stop(); + editor.inVirtualSelectionMode = true; + var initialRange; + var rectSel = []; + var blockSelect = function() { + var newCursor = editor.renderer.pixelToScreenCoordinates(mouseX, mouseY); + var cursor = session.screenToDocumentPosition(newCursor.row, newCursor.column, newCursor.offsetX); + if (isSamePoint(screenCursor, newCursor) && isSamePoint(cursor, selection.lead)) + return; + screenCursor = newCursor; + editor.selection.moveToPosition(cursor); + editor.renderer.scrollCursorIntoView(); + editor.removeSelectionMarkers(rectSel); + rectSel = selection.rectangularRangeBlock(screenCursor, screenAnchor); + if (editor.$mouseHandler.$clickSelection && rectSel.length == 1 && rectSel[0].isEmpty()) + rectSel[0] = editor.$mouseHandler.$clickSelection.clone(); + rectSel.forEach(editor.addSelectionMarker, editor); + editor.updateSelectionMarkers(); + }; + if (isMultiSelect && !accel) { + selection.toSingleRange(); + } else if (!isMultiSelect && accel) { + initialRange = selection.toOrientedRange(); + editor.addSelectionMarker(initialRange); + } + if (shift) + screenAnchor = session.documentToScreenPosition(selection.lead); + else + selection.moveToPosition(pos); + screenCursor = { + row: -1, + column: -1 + }; + var onMouseSelectionEnd = function(e) { + blockSelect(); + clearInterval(timerId); + editor.removeSelectionMarkers(rectSel); + if (!rectSel.length) + rectSel = [selection.toOrientedRange()]; + if (initialRange) { + editor.removeSelectionMarker(initialRange); + selection.toSingleRange(initialRange); + } + for (var i = 0; i < rectSel.length; i++) + selection.addRange(rectSel[i]); + editor.inVirtualSelectionMode = false; + editor.$mouseHandler.$clickSelection = null; + }; + var onSelectionInterval = blockSelect; + event.capture(editor.container, onMouseSelection, onMouseSelectionEnd); + var timerId = setInterval(function() { + onSelectionInterval(); + }, 20); + return e.preventDefault(); + } + } + exports.onMouseDown = onMouseDown; + +}); + +define("ace/commands/multi_select_commands", ["require", "exports", "module", "ace/keyboard/hash_handler"], function(require, exports, module) { + /** + * commands to enter multiselect mode + * @type {import("../../ace-internal").Ace.Command[]} + */ + exports.defaultCommands = [{ + name: "addCursorAbove", + description: "Add cursor above", + exec: function(editor) { + editor.selectMoreLines(-1); + }, + bindKey: { + win: "Ctrl-Alt-Up", + mac: "Ctrl-Alt-Up" + }, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "addCursorBelow", + description: "Add cursor below", + exec: function(editor) { + editor.selectMoreLines(1); + }, + bindKey: { + win: "Ctrl-Alt-Down", + mac: "Ctrl-Alt-Down" + }, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "addCursorAboveSkipCurrent", + description: "Add cursor above (skip current)", + exec: function(editor) { + editor.selectMoreLines(-1, true); + }, + bindKey: { + win: "Ctrl-Alt-Shift-Up", + mac: "Ctrl-Alt-Shift-Up" + }, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "addCursorBelowSkipCurrent", + description: "Add cursor below (skip current)", + exec: function(editor) { + editor.selectMoreLines(1, true); + }, + bindKey: { + win: "Ctrl-Alt-Shift-Down", + mac: "Ctrl-Alt-Shift-Down" + }, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectMoreBefore", + description: "Select more before", + exec: function(editor) { + editor.selectMore(-1); + }, + bindKey: { + win: "Ctrl-Alt-Left", + mac: "Ctrl-Alt-Left" + }, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectMoreAfter", + description: "Select more after", + exec: function(editor) { + editor.selectMore(1); + }, + bindKey: { + win: "Ctrl-Alt-Right", + mac: "Ctrl-Alt-Right" + }, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectNextBefore", + description: "Select next before", + exec: function(editor) { + editor.selectMore(-1, true); + }, + bindKey: { + win: "Ctrl-Alt-Shift-Left", + mac: "Ctrl-Alt-Shift-Left" + }, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "selectNextAfter", + description: "Select next after", + exec: function(editor) { + editor.selectMore(1, true); + }, + bindKey: { + win: "Ctrl-Alt-Shift-Right", + mac: "Ctrl-Alt-Shift-Right" + }, + scrollIntoView: "cursor", + readOnly: true + }, { + name: "toggleSplitSelectionIntoLines", + description: "Split selection into lines", + exec: function(editor) { + if (editor.multiSelect.rangeCount > 1) + editor.multiSelect.joinSelections(); + else + editor.multiSelect.splitIntoLines(); + }, + bindKey: { + win: "Ctrl-Alt-L", + mac: "Ctrl-Alt-L" + }, + readOnly: true + }, { + name: "splitSelectionIntoLines", + description: "Split into lines", + exec: function(editor) { + editor.multiSelect.splitIntoLines(); + }, + readOnly: true + }, { + name: "alignCursors", + description: "Align cursors", + exec: function(editor) { + editor.alignCursors(); + }, + bindKey: { + win: "Ctrl-Alt-A", + mac: "Ctrl-Alt-A" + }, + scrollIntoView: "cursor" + }, { + name: "findAll", + description: "Find all", + exec: function(editor) { + editor.findAll(); + }, + bindKey: { + win: "Ctrl-Alt-K", + mac: "Ctrl-Alt-G" + }, + scrollIntoView: "cursor", + readOnly: true + }]; + exports.multiSelectCommands = [{ + name: "singleSelection", + description: "Single selection", + bindKey: "esc", + exec: function(editor) { + editor.exitMultiSelectMode(); + }, + scrollIntoView: "cursor", + readOnly: true, + isAvailable: function(editor) { + return editor && editor.inMultiSelectMode; + } + }]; + var HashHandler = require("../keyboard/hash_handler").HashHandler; + exports.keyboardHandler = new HashHandler(exports.multiSelectCommands); + +}); + +define("ace/multi_select", ["require", "exports", "module", "ace/range_list", "ace/range", "ace/selection", "ace/mouse/multi_select_handler", "ace/lib/event", "ace/lib/lang", "ace/commands/multi_select_commands", "ace/search", "ace/edit_session", "ace/editor", "ace/config"], function(require, exports, module) { + /** + * @typedef {import("./anchor").Anchor} Anchor + * @typedef {import("../ace-internal").Ace.Point} Point + * @typedef {import("../ace-internal").Ace.ScreenCoordinates} ScreenCoordinates + */ + var RangeList = require("./range_list").RangeList; + var Range = require("./range").Range; + var Selection = require("./selection").Selection; + var onMouseDown = require("./mouse/multi_select_handler").onMouseDown; + var event = require("./lib/event"); + var lang = require("./lib/lang"); + var commands = require("./commands/multi_select_commands"); + exports.commands = commands.defaultCommands.concat(commands.multiSelectCommands); + var Search = require("./search").Search; + var search = new Search(); + + function find(session, needle, dir) { + search.$options.wrap = true; + search.$options.needle = needle; + search.$options.backwards = dir == -1; + return search.find(session); + } + var EditSession = require("./edit_session").EditSession; + (function() { + this.getSelectionMarkers = function() { + return this.$selectionMarkers; + }; + }).call(EditSession.prototype); + (function() { + this.ranges = null; + this.rangeList = null; + this.addRange = function(range, $blockChangeEvents) { + if (!range) + return; + if (!this.inMultiSelectMode && this.rangeCount === 0) { + var oldRange = this.toOrientedRange(); + this.rangeList.add(oldRange); + this.rangeList.add(range); + if (this.rangeList.ranges.length != 2) { + this.rangeList.removeAll(); + return $blockChangeEvents || this.fromOrientedRange(range); + } + this.rangeList.removeAll(); + this.rangeList.add(oldRange); + this.$onAddRange(oldRange); + } + if (!range.cursor) + range.cursor = range.end; + var removed = this.rangeList.add(range); + this.$onAddRange(range); + if (removed.length) + this.$onRemoveRange(removed); + if (this.rangeCount > 1 && !this.inMultiSelectMode) { + this._signal("multiSelect"); + this.inMultiSelectMode = true; + this.session.$undoSelect = false; + this.rangeList.attach(this.session); + } + return $blockChangeEvents || this.fromOrientedRange(range); + }; + this.toSingleRange = function(range) { + range = range || this.ranges[0]; + var removed = this.rangeList.removeAll(); + if (removed.length) + this.$onRemoveRange(removed); + range && this.fromOrientedRange(range); + }; + this.substractPoint = function(pos) { + var removed = this.rangeList.substractPoint(pos); + if (removed) { + this.$onRemoveRange(removed); + return removed[0]; + } + }; + this.mergeOverlappingRanges = function() { + var removed = this.rangeList.merge(); + if (removed.length) + this.$onRemoveRange(removed); + }; + this.$onAddRange = function(range) { + this.rangeCount = this.rangeList.ranges.length; + this.ranges.unshift(range); + this._signal("addRange", { + range: range + }); + }; + this.$onRemoveRange = function(removed) { + this.rangeCount = this.rangeList.ranges.length; + if (this.rangeCount == 1 && this.inMultiSelectMode) { + var lastRange = this.rangeList.ranges.pop(); + removed.push(lastRange); + this.rangeCount = 0; + } + for (var i = removed.length; i--;) { + var index = this.ranges.indexOf(removed[i]); + this.ranges.splice(index, 1); + } + this._signal("removeRange", { + ranges: removed + }); + if (this.rangeCount === 0 && this.inMultiSelectMode) { + this.inMultiSelectMode = false; + this._signal("singleSelect"); + this.session.$undoSelect = true; + this.rangeList.detach(this.session); + } + lastRange = lastRange || this.ranges[0]; + if (lastRange && !lastRange.isEqual(this.getRange())) + this.fromOrientedRange(lastRange); + }; + this.$initRangeList = function() { + if (this.rangeList) + return; + this.rangeList = new RangeList(); + this.ranges = []; + this.rangeCount = 0; + }; + this.getAllRanges = function() { + return this.rangeCount ? this.rangeList.ranges.concat() : [this.getRange()]; + }; + this.splitIntoLines = function() { + var ranges = this.ranges.length ? this.ranges : [this.getRange()]; + var newRanges = []; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + var row = range.start.row; + var endRow = range.end.row; + if (row === endRow) { + newRanges.push(range.clone()); + } else { + newRanges.push(new Range(row, range.start.column, row, this.session.getLine(row).length)); + while (++row < endRow) + newRanges.push(this.getLineRange(row, true)); + newRanges.push(new Range(endRow, 0, endRow, range.end.column)); + } + if (i == 0 && !this.isBackwards()) + newRanges = newRanges.reverse(); + } + this.toSingleRange(); + for (var i = newRanges.length; i--;) + this.addRange(newRanges[i]); + }; + this.joinSelections = function() { + var ranges = this.rangeList.ranges; + var lastRange = ranges[ranges.length - 1]; + var range = Range.fromPoints(ranges[0].start, lastRange.end); + this.toSingleRange(); + this.setSelectionRange(range, lastRange.cursor == lastRange.start); + }; + this.toggleBlockSelection = function() { + if (this.rangeCount > 1) { + var ranges = this.rangeList.ranges; + var lastRange = ranges[ranges.length - 1]; + var range = Range.fromPoints(ranges[0].start, lastRange.end); + this.toSingleRange(); + this.setSelectionRange(range, lastRange.cursor == lastRange.start); + } else { + var cursor = this.session.documentToScreenPosition(this.cursor); + var anchor = this.session.documentToScreenPosition(this.anchor); + var rectSel = this.rectangularRangeBlock(cursor, anchor); + rectSel.forEach(this.addRange, this); + } + }; + this.rectangularRangeBlock = function(screenCursor, screenAnchor, includeEmptyLines) { + var rectSel = []; + var xBackwards = screenCursor.column < screenAnchor.column; + if (xBackwards) { + var startColumn = screenCursor.column; + var endColumn = screenAnchor.column; + var startOffsetX = screenCursor.offsetX; + var endOffsetX = screenAnchor.offsetX; + } else { + var startColumn = screenAnchor.column; + var endColumn = screenCursor.column; + var startOffsetX = screenAnchor.offsetX; + var endOffsetX = screenCursor.offsetX; + } + var yBackwards = screenCursor.row < screenAnchor.row; + if (yBackwards) { + var startRow = screenCursor.row; + var endRow = screenAnchor.row; + } else { + var startRow = screenAnchor.row; + var endRow = screenCursor.row; + } + if (startColumn < 0) + startColumn = 0; + if (startRow < 0) + startRow = 0; + if (startRow == endRow) + includeEmptyLines = true; + var docEnd; + for (var row = startRow; row <= endRow; row++) { + var range = Range.fromPoints(this.session.screenToDocumentPosition(row, startColumn, startOffsetX), this.session.screenToDocumentPosition(row, endColumn, endOffsetX)); + if (range.isEmpty()) { + if (docEnd && isSamePoint(range.end, docEnd)) + break; + docEnd = range.end; + } + range.cursor = xBackwards ? range.start : range.end; + rectSel.push(range); + } + if (yBackwards) + rectSel.reverse(); + if (!includeEmptyLines) { + var end = rectSel.length - 1; + while (rectSel[end].isEmpty() && end > 0) + end--; + if (end > 0) { + var start = 0; + while (rectSel[start].isEmpty()) + start++; + } + for (var i = end; i >= start; i--) { + if (rectSel[i].isEmpty()) + rectSel.splice(i, 1); + } + } + return rectSel; + }; + }).call(Selection.prototype); + var Editor = require("./editor").Editor; + (function() { + this.updateSelectionMarkers = function() { + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + this.addSelectionMarker = function(orientedRange) { + if (!orientedRange.cursor) + orientedRange.cursor = orientedRange.end; + var style = this.getSelectionStyle(); + orientedRange.marker = this.session.addMarker(orientedRange, "ace_selection", style); + this.session.$selectionMarkers.push(orientedRange); + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + return orientedRange; + }; + this.removeSelectionMarker = function(range) { + if (!range.marker) + return; + this.session.removeMarker(range.marker); + var index = this.session.$selectionMarkers.indexOf(range); + if (index != -1) + this.session.$selectionMarkers.splice(index, 1); + this.session.selectionMarkerCount = this.session.$selectionMarkers.length; + }; + this.removeSelectionMarkers = function(ranges) { + var markerList = this.session.$selectionMarkers; + for (var i = ranges.length; i--;) { + var range = ranges[i]; + if (!range.marker) + continue; + this.session.removeMarker(range.marker); + var index = markerList.indexOf(range); + if (index != -1) + markerList.splice(index, 1); + } + this.session.selectionMarkerCount = markerList.length; + }; + this.$onAddRange = function(e) { + this.addSelectionMarker(e.range); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + this.$onRemoveRange = function(e) { + this.removeSelectionMarkers(e.ranges); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + this.$onMultiSelect = function(e) { + if (this.inMultiSelectMode) + return; + this.inMultiSelectMode = true; + this.setStyle("ace_multiselect"); + this.keyBinding.addKeyboardHandler(commands.keyboardHandler); + this.commands.setDefaultHandler("exec", this.$onMultiSelectExec); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + }; + this.$onSingleSelect = function(e) { + if (this.session.multiSelect.inVirtualMode) + return; + this.inMultiSelectMode = false; + this.unsetStyle("ace_multiselect"); + this.keyBinding.removeKeyboardHandler(commands.keyboardHandler); + this.commands.removeDefaultHandler("exec", this.$onMultiSelectExec); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + this._emit("changeSelection"); + }; + this.$onMultiSelectExec = function(e) { + var command = e.command; + var editor = e.editor; + if (!editor.multiSelect) + return; + if (!command.multiSelectAction) { + var result = command.exec(editor, e.args || {}); + editor.multiSelect.addRange(editor.multiSelect.toOrientedRange()); + editor.multiSelect.mergeOverlappingRanges(); + } else if (command.multiSelectAction == "forEach") { + result = editor.forEachSelection(command, e.args); + } else if (command.multiSelectAction == "forEachLine") { + result = editor.forEachSelection(command, e.args, true); + } else if (command.multiSelectAction == "single") { + editor.exitMultiSelectMode(); + result = command.exec(editor, e.args || {}); + } else { + result = command.multiSelectAction(editor, e.args || {}); + } + return result; + }; + this.forEachSelection = function(cmd, args, options) { + if (this.inVirtualSelectionMode) + return; + var keepOrder = options && options.keepOrder; + var $byLines = options == true || options && options.$byLines; + var session = this.session; + var selection = this.selection; + var rangeList = selection.rangeList; + var ranges = (keepOrder ? selection : rangeList).ranges; + var result; + if (!ranges.length) + return cmd.exec ? cmd.exec(this, args || {}) : cmd(this, args || {}); + var reg = selection._eventRegistry; + selection._eventRegistry = {}; + var tmpSel = new Selection(session); + this.inVirtualSelectionMode = true; + for (var i = ranges.length; i--;) { + if ($byLines) { + while (i > 0 && ranges[i].start.row == ranges[i - 1].end.row) + i--; + } + tmpSel.fromOrientedRange(ranges[i]); + tmpSel.index = i; + this.selection = session.selection = tmpSel; + var cmdResult = cmd.exec ? cmd.exec(this, args || {}) : cmd(this, args || {}); + if (!result && cmdResult !== undefined) + result = cmdResult; + tmpSel.toOrientedRange(ranges[i]); + } + tmpSel.detach(); + this.selection = session.selection = selection; + this.inVirtualSelectionMode = false; + selection._eventRegistry = reg; + selection.mergeOverlappingRanges(); + if (selection.ranges[0]) + selection.fromOrientedRange(selection.ranges[0]); + var anim = this.renderer.$scrollAnimation; + this.onCursorChange(); + this.onSelectionChange(); + if (anim && anim.from == anim.to) + this.renderer.animateScrolling(anim.from); + return result; + }; + this.exitMultiSelectMode = function() { + if (!this.inMultiSelectMode || this.inVirtualSelectionMode) + return; + this.multiSelect.toSingleRange(); + }; + this.getSelectedText = function() { + var text = ""; + if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { + var ranges = this.multiSelect.rangeList.ranges; + var buf = []; + for (var i = 0; i < ranges.length; i++) { + buf.push(this.session.getTextRange(ranges[i])); + } + var nl = this.session.getDocument().getNewLineCharacter(); + text = buf.join(nl); + if (text.length == (buf.length - 1) * nl.length) + text = ""; + } else if (!this.selection.isEmpty()) { + text = this.session.getTextRange(this.getSelectionRange()); + } + return text; + }; + this.$checkMultiselectChange = function(e, anchor) { + if (this.inMultiSelectMode && !this.inVirtualSelectionMode) { + var range = this.multiSelect.ranges[0]; + if (this.multiSelect.isEmpty() && anchor == this.multiSelect.anchor) + return; + var pos = anchor == this.multiSelect.anchor ? + range.cursor == range.start ? range.end : range.start : + range.cursor; + if (pos.row != anchor.row || + this.session.$clipPositionToDocument(pos.row, pos.column).column != anchor.column) + this.multiSelect.toSingleRange(this.multiSelect.toOrientedRange()); + else + this.multiSelect.mergeOverlappingRanges(); + } + }; + this.findAll = function(needle, options, additive) { + options = options || {}; + options.needle = needle || options.needle; + if (options.needle == undefined) { + var range = this.selection.isEmpty() ? + this.selection.getWordRange() : + this.selection.getRange(); + options.needle = this.session.getTextRange(range); + } + this.$search.set(options); + var ranges = this.$search.findAll(this.session); + if (!ranges.length) + return 0; + var selection = this.multiSelect; + if (!additive) + selection.toSingleRange(ranges[0]); + for (var i = ranges.length; i--;) + selection.addRange(ranges[i], true); + if (range && selection.rangeList.rangeAtPoint(range.start)) + selection.addRange(range, true); + return ranges.length; + }; + this.selectMoreLines = function(dir, skip) { + var range = this.selection.toOrientedRange(); + var isBackwards = range.cursor == range.end; + var screenLead = this.session.documentToScreenPosition(range.cursor); + if (this.selection.$desiredColumn) + screenLead.column = this.selection.$desiredColumn; + var lead = this.session.screenToDocumentPosition(screenLead.row + dir, screenLead.column); + if (!range.isEmpty()) { + var screenAnchor = this.session.documentToScreenPosition(isBackwards ? range.end : range.start); + var anchor = this.session.screenToDocumentPosition(screenAnchor.row + dir, screenAnchor.column); + } else { + var anchor = lead; + } + if (isBackwards) { + var newRange = Range.fromPoints(lead, anchor); + newRange.cursor = newRange.start; + } else { + var newRange = Range.fromPoints(anchor, lead); + newRange.cursor = newRange.end; + } + newRange.desiredColumn = screenLead.column; + if (!this.selection.inMultiSelectMode) { + this.selection.addRange(range); + } else { + if (skip) + var toRemove = range.cursor; + } + this.selection.addRange(newRange); + if (toRemove) + this.selection.substractPoint(toRemove); + }; + this.transposeSelections = function(dir) { + var session = this.session; + var sel = session.multiSelect; + var all = sel.ranges; + for (var i = all.length; i--;) { + var range = all[i]; + if (range.isEmpty()) { + var tmp_1 = session.getWordRange(range.start.row, range.start.column); + range.start.row = tmp_1.start.row; + range.start.column = tmp_1.start.column; + range.end.row = tmp_1.end.row; + range.end.column = tmp_1.end.column; + } + } + sel.mergeOverlappingRanges(); + var words = []; + for (var i = all.length; i--;) { + var range = all[i]; + words.unshift(session.getTextRange(range)); + } + if (dir < 0) + words.unshift(words.pop()); + else + words.push(words.shift()); + for (var i = all.length; i--;) { + var range = all[i]; + var tmp = range.clone(); + session.replace(range, words[i]); + range.start.row = tmp.start.row; + range.start.column = tmp.start.column; + } + sel.fromOrientedRange(sel.ranges[0]); + }; + this.selectMore = function(dir, skip, stopAtFirst) { + var session = this.session; + var sel = session.multiSelect; + var range = sel.toOrientedRange(); + if (range.isEmpty()) { + range = session.getWordRange(range.start.row, range.start.column); + range.cursor = dir == -1 ? range.start : range.end; + this.multiSelect.addRange(range); + if (stopAtFirst) + return; + } + var needle = session.getTextRange(range); + var newRange = find(session, needle, dir); + if (newRange) { + newRange.cursor = dir == -1 ? newRange.start : newRange.end; + this.session.unfold(newRange); + this.multiSelect.addRange(newRange); + this.renderer.scrollCursorIntoView(null, 0.5); + } + if (skip) + this.multiSelect.substractPoint(range.cursor); + }; + this.alignCursors = function() { + var session = this.session; + var sel = session.multiSelect; + var ranges = sel.ranges; + var row = -1; + var sameRowRanges = ranges.filter(function(r) { + if (r.cursor.row == row) + return true; + row = r.cursor.row; + }); + if (!ranges.length || sameRowRanges.length == ranges.length - 1) { + var range = this.selection.getRange(); + var fr = range.start.row, + lr = range.end.row; + var guessRange = fr == lr; + if (guessRange) { + var max = this.session.getLength(); + var line; + do { + line = this.session.getLine(lr); + } while (/[=:]/.test(line) && ++lr < max); + do { + line = this.session.getLine(fr); + } while (/[=:]/.test(line) && --fr > 0); + if (fr < 0) + fr = 0; + if (lr >= max) + lr = max - 1; + } + var lines = this.session.removeFullLines(fr, lr); + lines = this.$reAlignText(lines, guessRange); + this.session.insert({ + row: fr, + column: 0 + }, lines.join("\n") + "\n"); + if (!guessRange) { + range.start.column = 0; + range.end.column = lines[lines.length - 1].length; + } + this.selection.setRange(range); + } else { + sameRowRanges.forEach(function(r) { + sel.substractPoint(r.cursor); + }); + var maxCol = 0; + var minSpace = Infinity; + var spaceOffsets = ranges.map(function(r) { + var p = r.cursor; + var line = session.getLine(p.row); + var spaceOffset = line.substr(p.column).search(/\S/g); + if (spaceOffset == -1) + spaceOffset = 0; + if (p.column > maxCol) + maxCol = p.column; + if (spaceOffset < minSpace) + minSpace = spaceOffset; + return spaceOffset; + }); + ranges.forEach(function(r, i) { + var p = r.cursor; + var l = maxCol - p.column; + var d = spaceOffsets[i] - minSpace; + if (l > d) + session.insert(p, lang.stringRepeat(" ", l - d)); + else + session.remove(new Range(p.row, p.column, p.row, p.column - l + d)); + r.start.column = r.end.column = maxCol; + r.start.row = r.end.row = p.row; + r.cursor = r.end; + }); + sel.fromOrientedRange(ranges[0]); + this.renderer.updateCursor(); + this.renderer.updateBackMarkers(); + } + }; + this.$reAlignText = function(lines, forceLeft) { + var isLeftAligned = true, + isRightAligned = true; + var startW, textW, endW; + return lines.map(function(line) { + var m = line.match(/(\s*)(.*?)(\s*)([=:].*)/); + if (!m) + return [line]; + if (startW == null) { + startW = m[1].length; + textW = m[2].length; + endW = m[3].length; + return m; + } + if (startW + textW + endW != m[1].length + m[2].length + m[3].length) + isRightAligned = false; + if (startW != m[1].length) + isLeftAligned = false; + if (startW > m[1].length) + startW = m[1].length; + if (textW < m[2].length) + textW = m[2].length; + if (endW > m[3].length) + endW = m[3].length; + return m; + }).map(forceLeft ? alignLeft : + isLeftAligned ? isRightAligned ? alignRight : alignLeft : unAlign); + + function spaces(n) { + return lang.stringRepeat(" ", n); + } + + function alignLeft(m) { + return !m[2] ? m[0] : spaces(startW) + m[2] + + spaces(textW - m[2].length + endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + + function alignRight(m) { + return !m[2] ? m[0] : spaces(startW + textW - m[2].length) + m[2] + + spaces(endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + + function unAlign(m) { + return !m[2] ? m[0] : spaces(startW) + m[2] + + spaces(endW) + + m[4].replace(/^([=:])\s+/, "$1 "); + } + }; + }).call(Editor.prototype); + + function isSamePoint(p1, p2) { + return p1.row == p2.row && p1.column == p2.column; + } + exports.onSessionChange = function(e) { + var session = e.session; + if (session && !session.multiSelect) { + session.$selectionMarkers = []; + session.selection.$initRangeList(); + session.multiSelect = session.selection; + } + this.multiSelect = session && session.multiSelect; + var oldSession = e.oldSession; + if (oldSession) { + oldSession.multiSelect.off("addRange", this.$onAddRange); + oldSession.multiSelect.off("removeRange", this.$onRemoveRange); + oldSession.multiSelect.off("multiSelect", this.$onMultiSelect); + oldSession.multiSelect.off("singleSelect", this.$onSingleSelect); + oldSession.multiSelect.lead.off("change", this.$checkMultiselectChange); + oldSession.multiSelect.anchor.off("change", this.$checkMultiselectChange); + } + if (session) { + session.multiSelect.on("addRange", this.$onAddRange); + session.multiSelect.on("removeRange", this.$onRemoveRange); + session.multiSelect.on("multiSelect", this.$onMultiSelect); + session.multiSelect.on("singleSelect", this.$onSingleSelect); + session.multiSelect.lead.on("change", this.$checkMultiselectChange); + session.multiSelect.anchor.on("change", this.$checkMultiselectChange); + } + if (session && this.inMultiSelectMode != session.selection.inMultiSelectMode) { + if (session.selection.inMultiSelectMode) + this.$onMultiSelect(); + else + this.$onSingleSelect(); + } + }; + + function MultiSelect(editor) { + if (editor.$multiselectOnSessionChange) + return; + editor.$onAddRange = editor.$onAddRange.bind(editor); + editor.$onRemoveRange = editor.$onRemoveRange.bind(editor); + editor.$onMultiSelect = editor.$onMultiSelect.bind(editor); + editor.$onSingleSelect = editor.$onSingleSelect.bind(editor); + editor.$multiselectOnSessionChange = exports.onSessionChange.bind(editor); + editor.$checkMultiselectChange = editor.$checkMultiselectChange.bind(editor); + editor.$multiselectOnSessionChange(editor); + editor.on("changeSession", editor.$multiselectOnSessionChange); + editor.on("mousedown", onMouseDown); + editor.commands.addCommands(commands.defaultCommands); + addAltCursorListeners(editor); + } + + function addAltCursorListeners(editor) { + if (!editor.textInput) + return; + var el = editor.textInput.getElement(); + var altCursor = false; + event.addListener(el, "keydown", function(e) { + var altDown = e.keyCode == 18 && !(e.ctrlKey || e.shiftKey || e.metaKey); + if (editor.$blockSelectEnabled && altDown) { + if (!altCursor) { + editor.renderer.setMouseCursor("crosshair"); + altCursor = true; + } + } else if (altCursor) { + reset(); + } + }, editor); + event.addListener(el, "keyup", reset, editor); + event.addListener(el, "blur", reset, editor); + + function reset(e) { + if (altCursor) { + editor.renderer.setMouseCursor(""); + altCursor = false; + } + } + } + exports.MultiSelect = MultiSelect; + require("./config").defineOptions(Editor.prototype, "editor", { + enableMultiselect: { + set: function(val) { + MultiSelect(this); + if (val) { + this.on("mousedown", onMouseDown); + } else { + this.off("mousedown", onMouseDown); + } + }, + value: true + }, + enableBlockSelect: { + set: function(val) { + this.$blockSelectEnabled = val; + }, + value: true + } + }); + +}); + +define("ace/mode/folding/fold_mode", ["require", "exports", "module", "ace/range"], function(require, exports, module) { + "use strict"; + var Range = require("../../range").Range; + var FoldMode = exports.FoldMode = function() {}; + (function() { + this.foldingStartMarker = null; + this.foldingStopMarker = null; + this.getFoldWidget = function(session, foldStyle, row) { + var line = session.getLine(row); + if (this.foldingStartMarker.test(line)) + return "start"; + if (foldStyle == "markbeginend" && + this.foldingStopMarker && + this.foldingStopMarker.test(line)) + return "end"; + return ""; + }; + this.getFoldWidgetRange = function(session, foldStyle, row) { + return null; + }; + this.indentationBlock = function(session, row, column) { + var re = /\S/; + var line = session.getLine(row); + var startLevel = line.search(re); + if (startLevel == -1) + return; + var startColumn = column || line.length; + var maxRow = session.getLength(); + var startRow = row; + var endRow = row; + while (++row < maxRow) { + var level = session.getLine(row).search(re); + if (level == -1) + continue; + if (level <= startLevel) { + var token = session.getTokenAt(row, 0); + if (!token || token.type !== "string") + break; + } + endRow = row; + } + if (endRow > startRow) { + var endColumn = session.getLine(endRow).length; + return new Range(startRow, startColumn, endRow, endColumn); + } + }; + this.openingBracketBlock = function(session, bracket, row, column, typeRe) { + var start = { + row: row, + column: column + 1 + }; + var end = session.$findClosingBracket(bracket, start, typeRe); + if (!end) + return; + var fw = session.foldWidgets[end.row]; + if (fw == null) + fw = session.getFoldWidget(end.row); + if (fw == "start" && end.row > start.row) { + end.row--; + end.column = session.getLine(end.row).length; + } + return Range.fromPoints(start, end); + }; + this.closingBracketBlock = function(session, bracket, row, column, typeRe) { + var end = { + row: row, + column: column + }; + var start = session.$findOpeningBracket(bracket, end); + if (!start) + return; + start.column++; + end.column--; + return Range.fromPoints(start, end); + }; + }).call(FoldMode.prototype); + +}); + +define("ace/ext/error_marker", ["require", "exports", "module", "ace/line_widgets", "ace/lib/dom", "ace/range", "ace/config"], function(require, exports, module) { + "use strict"; + var LineWidgets = require("../line_widgets").LineWidgets; + var dom = require("../lib/dom"); + var Range = require("../range").Range; + var nls = require("../config").nls; + + function binarySearch(array, needle, comparator) { + var first = 0; + var last = array.length - 1; + while (first <= last) { + var mid = (first + last) >> 1; + var c = comparator(needle, array[mid]); + if (c > 0) + first = mid + 1; + else if (c < 0) + last = mid - 1; + else + return mid; + } + return -(first + 1); + } + + function findAnnotations(session, row, dir) { + var annotations = session.getAnnotations().sort(Range.comparePoints); + if (!annotations.length) + return; + var i = binarySearch(annotations, { + row: row, + column: -1 + }, Range.comparePoints); + if (i < 0) + i = -i - 1; + if (i >= annotations.length) + i = dir > 0 ? 0 : annotations.length - 1; + else if (i === 0 && dir < 0) + i = annotations.length - 1; + var annotation = annotations[i]; + if (!annotation || !dir) + return; + if (annotation.row === row) { + do { + annotation = annotations[i += dir]; + } while (annotation && annotation.row === row); + if (!annotation) + return annotations.slice(); + } + var matched = []; + row = annotation.row; + do { + matched[dir < 0 ? "unshift" : "push"](annotation); + annotation = annotations[i += dir]; + } while (annotation && annotation.row == row); + return matched.length && matched; + } + exports.showErrorMarker = function(editor, dir) { + var session = editor.session; + if (!session.widgetManager) { + session.widgetManager = new LineWidgets(session); + session.widgetManager.attach(editor); + } + var pos = editor.getCursorPosition(); + var row = pos.row; + var oldWidget = session.widgetManager.getWidgetsAtRow(row).filter(function(w) { + return w.type == "errorMarker"; + })[0]; + if (oldWidget) { + oldWidget.destroy(); + } else { + row -= dir; + } + var annotations = findAnnotations(session, row, dir); + var gutterAnno; + if (annotations) { + var annotation = annotations[0]; + pos.column = (annotation.pos && typeof annotation.column != "number" ? + annotation.pos.sc : + annotation.column) || 0; + pos.row = annotation.row; + gutterAnno = editor.renderer.$gutterLayer.$annotations[pos.row]; + } else if (oldWidget) { + return; + } else { + gutterAnno = { + displayText: [nls("error-marker.good-state", "Looks good!")], + className: "ace_ok" + }; + } + editor.session.unfold(pos.row); + editor.selection.moveToPosition(pos); + var w = { + row: pos.row, + fixedWidth: true, + coverGutter: true, + el: dom.createElement("div"), + type: "errorMarker" + }; + var el = w.el.appendChild(dom.createElement("div")); + var arrow = w.el.appendChild(dom.createElement("div")); + arrow.className = "error_widget_arrow " + gutterAnno.className; + var left = editor.renderer.$cursorLayer + .getPixelPosition(pos).left; + arrow.style.left = left + editor.renderer.gutterWidth - 5 + "px"; + w.el.className = "error_widget_wrapper"; + el.className = "error_widget " + gutterAnno.className; + gutterAnno.displayText.forEach(function(annoTextLine, i) { + el.appendChild(dom.createTextNode(annoTextLine)); + if (i < gutterAnno.displayText.length - 1) { + el.appendChild(dom.createElement("br")); + } + }); + el.appendChild(dom.createElement("div")); + var kb = function(_, hashId, keyString) { + if (hashId === 0 && (keyString === "esc" || keyString === "return")) { + w.destroy(); + return { + command: "null" + }; + } + }; + w.destroy = function() { + if (editor.$mouseHandler.isMousePressed) + return; + editor.keyBinding.removeKeyboardHandler(kb); + session.widgetManager.removeLineWidget(w); + editor.off("changeSelection", w.destroy); + editor.off("changeSession", w.destroy); + editor.off("mouseup", w.destroy); + editor.off("change", w.destroy); + }; + editor.keyBinding.addKeyboardHandler(kb); + editor.on("changeSelection", w.destroy); + editor.on("changeSession", w.destroy); + editor.on("mouseup", w.destroy); + editor.on("change", w.destroy); + editor.session.widgetManager.addLineWidget(w); + w.el.onmousedown = editor.focus.bind(editor); + editor.renderer.scrollCursorIntoView(null, 0.5, { + bottom: w.el.offsetHeight + }); + }; + dom.importCssString("\n .error_widget_wrapper {\n background: inherit;\n color: inherit;\n border:none\n }\n .error_widget {\n border-top: solid 2px;\n border-bottom: solid 2px;\n margin: 5px 0;\n padding: 10px 40px;\n white-space: pre-wrap;\n }\n .error_widget.ace_error, .error_widget_arrow.ace_error{\n border-color: #ff5a5a\n }\n .error_widget.ace_warning, .error_widget_arrow.ace_warning{\n border-color: #F1D817\n }\n .error_widget.ace_info, .error_widget_arrow.ace_info{\n border-color: #5a5a5a\n }\n .error_widget.ace_ok, .error_widget_arrow.ace_ok{\n border-color: #5aaa5a\n }\n .error_widget_arrow {\n position: absolute;\n border: solid 5px;\n border-top-color: transparent!important;\n border-right-color: transparent!important;\n border-left-color: transparent!important;\n top: -5px;\n }\n", "error_marker.css", false); + +}); + +define("ace/ace", ["require", "exports", "module", "ace/lib/dom", "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", "ace/loader_build"], function(require, exports, module) { + /** + * The main class required to set up an Ace instance in the browser. + * + * @namespace Ace + **/ + "use strict"; + require("./loader_build")(exports) + var dom = require("./lib/dom"); + var Range = require("./range").Range; + var Editor = require("./editor").Editor; + var EditSession = require("./edit_session").EditSession; + var UndoManager = require("./undomanager").UndoManager; + var Renderer = require("./virtual_renderer").VirtualRenderer; + require("./worker/worker_client"); + require("./keyboard/hash_handler"); + require("./placeholder"); + require("./multi_select"); + require("./mode/folding/fold_mode"); + require("./theme/textmate"); + require("./ext/error_marker"); + exports.config = require("./config"); + exports.edit = function(el, options) { + if (typeof el == "string") { + var _id = el; + el = document.getElementById(_id); + if (!el) + throw new Error("ace.edit can't find div #" + _id); + } + if (el && el.env && el.env.editor instanceof Editor) + return el.env.editor; + var value = ""; + if (el && /input|textarea/i.test(el.tagName)) { + var oldNode = el; + value = oldNode.value; + el = dom.createElement("pre"); + oldNode.parentNode.replaceChild(el, oldNode); + } else if (el) { + value = el.textContent; + el.innerHTML = ""; + } + var doc = exports.createEditSession(value); + var editor = new Editor(new Renderer(el), doc, options); + var env = { + document: doc, + editor: editor, + onResize: editor.resize.bind(editor, null) + }; + if (oldNode) + env.textarea = oldNode; + editor.on("destroy", function() { + env.editor.container.env = null; // prevent memory leak on old ie + }); + editor.container.env = editor.env = env; + return editor; + }; + exports.createEditSession = function(text, mode) { + var doc = new EditSession(text, mode); + doc.setUndoManager(new UndoManager()); + return doc; + }; + exports.Range = Range; + exports.Editor = Editor; + exports.EditSession = EditSession; + exports.UndoManager = UndoManager; + exports.VirtualRenderer = Renderer; + exports.version = exports.config.version; + +}); +(function() { + window.require(["ace/ace"], function(a) { + if (a) { + a.config.init(true); + a.define = window.define; + } + var global = (function() { + return this; + })(); + if (!global && typeof window != "undefined") global = window; // can happen in strict mode + if (!global && typeof self != "undefined") global = self; // can happen in webworker + + if (!global.ace) + global.ace = a; + for (var key in a) + if (a.hasOwnProperty(key)) + global.ace[key] = a[key]; + global.ace["default"] = global.ace; + if (typeof module == "object" && typeof exports == "object" && module) { + module.exports = global.ace; + } + }); +})(); diff --git a/src/plantuml_gui/static/favicon-32x32.png b/src/plantuml_gui/static/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..6145a7755f3851f15808e637355945ea0cf2f6c9 GIT binary patch literal 1309 zcmV+&1>*XNP)Px(*GWV{R9HvtmtRbiXB5VNpM`cng@P@Ar4`fx{+Y;7FJ?kAvkS9`3kxBOi7{rG z^U4cDG|_NlFdDruS;mFSV$96ubeEGQ#4QG*TsX{FT)<%pv_-HjLJP>B(sns-{U)0q zQ0IkN;z_=XuQ~5I&-7Yn9!47P-Mg2;!9lXKvy&z86O|VYPG5&Qgo@9ot;%VLqkK{zki<%8#b_Y>(=A}1OfrII2MaBH8q9P z=~TODYina>W`?S&D#gh9_3KrmNF;*AVo?oxdwW$gVNd`|t+cc>#fVWDG7t)d)Z&{r zZz=$lm6h1-b~-vbR6&!;#OUa#ddkYm@cDe|H=E5yI2>l*zJ02fuqX`H)zz_L#fszs z=$Sn|J*t7n;~^&}hugPrD_zKr=jP_f%F3d&v=pz`OKNJW0wB!Q)YK?u3JVL7aY~Mi zGie6&M4_|1yd1yZuL?*}d1N%Y7IZy`p)SroYmLPI+^4Gwar^qoJYUVRS1Sa&xWRaKau`NU$yCTun|w;OAAb^^fc(-5=Q3dl51FAKo$_>|lazB0@UT)c?m z(4l2(2rGe(4sv(yOaKUv`mo$^Vo8bP9fjgmFg67y;ZB2S46-v4b%QH+W8c$i44}Cg z$H|jC25f-1e-OwA9s%M6x*-RzUd7(f@C-oEgQe9)Gzvd;LsmMBguuBf(dWT1Y%7H= zP8iL2ANxl?8v{6X3P)2DcYwEm0YId<4fq9+`*0&rEDT(}jAQ@)e*zc?z@0y#(gmp| z==MM?o_OyYhPv$#OkWk}i0%uF)%T|PF;;+Ku?3I4pp}v15S`{O}%mb0VLmV`SsEgFNDcV<~xTeT5J4utLH!Qco;c<9xW`|Hvs8!;W~PhjLgI)-uyRw?F{?{J~m~) T#QW>n00000NkvXXu0mjfMxANg literal 0 HcmV?d00001 diff --git a/src/plantuml_gui/static/mode-plantuml.js b/src/plantuml_gui/static/mode-plantuml.js new file mode 100644 index 0000000..aa4e115 --- /dev/null +++ b/src/plantuml_gui/static/mode-plantuml.js @@ -0,0 +1,204 @@ +ace.define("ace/mode/plantuml", [ + "require", "exports", "module", "ace/lib/oop", "ace/mode/text", "ace/tokenizer", "ace/mode/plantuml_highlight_rules", "ace/range" +], function(require, exports, module) { + "use strict"; + + var oop = require("../lib/oop"); + var TextMode = require("./text").Mode; + var Tokenizer = require("../tokenizer").Tokenizer; + var PlantUMLHighlightRules = require("./plantuml_highlight_rules").PlantUmlHighlightRules; + var Range = require("../range").Range; + + var PlantUMLMode = function() { + this.HighlightRules = PlantUMLHighlightRules; + }; + + oop.inherits(PlantUMLMode, TextMode); + + (function() { + this.lineCommentStart = "--"; + this.$id = "ace/mode/plantuml"; + }).call(PlantUMLMode.prototype); + + exports.Mode = PlantUMLMode; +}); + +ace.define("ace/mode/plantuml_highlight_rules", [ + "require", "exports", "module", "ace/lib/oop", "ace/mode/text_highlight_rules" +], function(require, exports, module) { + "use strict"; + + var oop = require("../lib/oop"); + var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; + + var PlantUMLHighlightRules = function() { + var langKeywords = "(?:autonumber|skinparam|include|ifdef|define|endif)" + + "|top to bottom|direction|left to right|width|height" + + "|title|endtitle|note|end note|hide footbox|header|endheader|legend|endlegend|newpage" + + "|center|footer|scale|autonumber"; + + var skinKeywords = [ + "activityArrowColor", "activityArrowFontColor", "activityArrowFontName", + "activityArrowFontSize", "activityArrowFontStyle", "activityBackgroundColor", + "activityBarColor", "activityBorderColor", "activityEndColor", "activityFontColor", + "activityFontName", "activityFontSize", "activityFontStyle", "activityStartColor", + "backgroundColor", "circledCharacterFontColor", "circledCharacterFontName", + "circledCharacterFontSize", "circledCharacterFontStyle", "circledCharacterRadius", + "classArrowColor", "classArrowFontColor", "classArrowFontName", "classArrowFontSize", + "classArrowFontStyle", "classAttributeFontColor", "classAttributeFontName", + "classAttributeFontSize", "classAttributeFontStyle", "classAttributeIconSize", + "classBackgroundColor", "classBorderColor", "classFontColor", "classFontName", + "classFontSize", "classFontStyle", "classStereotypeFontColor", "classStereotypeFontName", + "classStereotypeFontSize", "classStereotypeFontStyle", "componentArrowColor", + "componentArrowFontColor", "componentArrowFontName", "componentArrowFontSize", + "componentArrowFontStyle", "componentBackgroundColor", "componentBorderColor", + "componentFontColor", "componentFontName", "componentFontSize", "componentFontStyle", + "componentInterfaceBackgroundColor", "componentInterfaceBorderColor", + "componentStereotypeFontColor", "componentStereotypeFontName", + "componentStereotypeFontSize", "componentStereotypeFontStyle", "footerFontColor", + "footerFontName", "footerFontSize", "footerFontStyle", "headerFontColor", "headerFontName", + "headerFontSize", "headerFontStyle", "noteBackgroundColor", "noteBorderColor", + "noteFontColor", "noteFontName", "noteFontSize", "noteFontStyle", "packageBackgroundColor", + "packageBorderColor", "packageFontColor", "packageFontName", "packageFontSize", + "packageFontStyle", "sequenceActorBackgroundColor", "sequenceActorBorderColor", + "sequenceActorFontColor", "sequenceActorFontName", "sequenceActorFontSize", + "sequenceActorFontStyle", "sequenceArrowColor", "sequenceArrowFontColor", + "sequenceArrowFontName", "sequenceArrowFontSize", "sequenceArrowFontStyle", + "sequenceDividerBackgroundColor", "sequenceDividerFontColor", "sequenceDividerFontName", + "sequenceDividerFontSize", "sequenceDividerFontStyle", "sequenceGroupBackgroundColor", + "sequenceGroupingFontColor", "sequenceGroupingFontName", "sequenceGroupingFontSize", + "sequenceGroupingFontStyle", "sequenceGroupingHeaderFontColor", + "sequenceGroupingHeaderFontName", "sequenceGroupingHeaderFontSize", + "sequenceGroupingHeaderFontStyle", "sequenceLifeLineBackgroundColor", + "sequenceLifeLineBorderColor", "sequenceParticipantBackgroundColor", + "sequenceParticipantBorderColor", "sequenceParticipantFontColor", + "sequenceParticipantFontName", "sequenceParticipantFontSize", + "sequenceParticipantFontStyle", "sequenceTitleFontColor", "sequenceTitleFontName", + "sequenceTitleFontSize", "sequenceTitleFontStyle", "stateArrowColor", + "stateArrowFontColor", "stateArrowFontName", "stateArrowFontSize", "stateArrowFontStyle", + "stateAttributeFontColor", "stateAttributeFontName", "stateAttributeFontSize", + "stateAttributeFontStyle", "stateBackgroundColor", "stateBorderColor", "stateEndColor", + "stateFontColor", "stateFontName", "stateFontSize", "stateFontStyle", "stateStartColor", + "stereotypeABackgroundColor", "stereotypeCBackgroundColor", + "stereotypeEBackgroundColor", "stereotypeIBackgroundColor", "titleFontColor", + "titleFontName", "titleFontSize", "titleFontStyle", "usecaseActorBackgroundColor", + "usecaseActorBorderColor", "usecaseActorFontColor", "usecaseActorFontName", + "usecaseActorFontSize", "usecaseActorFontStyle", "usecaseActorStereotypeFontColor", + "usecaseActorStereotypeFontName", "usecaseActorStereotypeFontSize", + "usecaseActorStereotypeFontStyle", "usecaseArrowColor", "usecaseArrowFontColor", + "usecaseArrowFontName", "usecaseArrowFontSize", "usecaseArrowFontStyle", + "usecaseBackgroundColor", "usecaseBorderColor", "usecaseFontColor", "usecaseFontName", + "usecaseFontSize", "usecaseFontStyle", "usecaseStereotypeFontColor", + "usecaseStereotypeFontName", "usecaseStereotypeFontSize", "usecaseStereotypeFontStyle", + "ActorBackgroundColor", "ActorBorderColor", "ActorFontColor", "ActorFontName", + "ActorFontSize", "ActorFontStyle", "ActorStereotypeFontColor", "ActorStereotypeFontName", + "ActorStereotypeFontSize", "ActorStereotypeFontStyle", "ArrowColor", "ArrowFontColor", + "ArrowFontName", "ArrowFontSize", "ArrowFontStyle", "AttributeFontColor", "AttributeFontName", + "AttributeFontSize", "AttributeFontStyle", "AttributeIconSize", "BackgroundColor", "BarColor", + "BorderColor", "CharacterFontColor", "CharacterFontName", "CharacterFontSize", + "CharacterFontStyle", "CharacterRadius", "Color", "DividerBackgroundColor", + "DividerFontColor", "DividerFontName", "DividerFontSize", "DividerFontStyle", "EndColor", + "FontColor", "FontName", "FontSize", "FontStyle", "GroupBackgroundColor", "GroupingFontColor", + "GroupingFontName", "GroupingFontSize", "GroupingFontStyle", "GroupingHeaderFontColor", + "GroupingHeaderFontName", "GroupingHeaderFontSize", "GroupingHeaderFontStyle", + "InterfaceBackgroundColor", "InterfaceBorderColor", "LifeLineBackgroundColor", + "LifeLineBorderColor", "ParticipantBackgroundColor", "ParticipantBorderColor", + "ParticipantFontColor", "ParticipantFontName", "ParticipantFontSize", + "ParticipantFontStyle", "StartColor", "stateArrowColor", "stereotypeABackgroundColor", + "stereotypeCBackgroundColor", "stereotypeEBackgroundColor", "StereotypeFontColor", + "StereotypeFontName", "StereotypeFontSize", "StereotypeFontStyle", + "stereotypeIBackgroundColor", "TitleFontColor", "TitleFontName", "TitleFontSize", "TitleFontStyle" + ].join("|"); + + var plantFunctions = [ + "actor", "boundary", "control", "entity", "database", "participant", + "alt", "else", "opt", "loop", "par", "break", "critical", "group", "box", "merge", + "rnote", "hnote", "right", "left", "top", "bottom", "over", "of", "end", + "activate", "create", "deactivate", "destroy", + "usecase", "rectangle", + "start", "stop", "if", "then", "else", "endif", "repeat", "while", "endwhile", + "fork", "switch", "case", "endswitch", "forkagain", "endfork", "partition", "\\|Swimlane\\w+\\|", "detach", + "class", "\\{(?:abstract|static|classifier)\\}", + "abstract", "interface", "annotation", "enum", "hide", + "hide empty (?:members|attributes|methods|circle|stereotype|fields)", + "show (?:members|attributes|methods|circle|stereotype|fields)", + "package(?:<<(?:Node|Rect|Folder|Frame|Cloud|Database)>>)?", + "namespace", "set\\snamespaceSeparator", + "package", "node", "folder", "frame", "cloud", "database", + "\\[\\*\\]", "state", "object" + ].join("|"); + + var constants = "right|left|up|down|over|of|as|is"; + + var keywordMapper = this.createKeywordMapper({ + "variable.language": langKeywords, + "variable.parameter": skinKeywords, + "keyword.function": plantFunctions, + "keyword.constant": constants, + "support.type": "bool(?:ean)?|string|int(?:eger)?|float|double|(?:var)?char|" + + "decimal|date|time|timestamp|array|void|none" + }, "text", true, "|"); + + this.$rules = { + "start": [{ + token: "string", + regex: '"', + next: "string" + }, + { + token: "string", + regex: ': .*$', + next: "start" + }, + { + token: "language.constant", + regex: "@startuml|@enduml", + next: "start" + }, + { + token: keywordMapper, + regex: "\\b\\w+\\b" + }, + { + token: "keyword", + regex: /^\\s*:[^\\/;|}{\\n]+[}{;\\\|\\/]$/mi + }, + { + token: "doc.comment", + regex: /^'.+/ + }, + { + token: "keyword.operator", + regex: /(:?[-\/o<]{1,2})?-(:?[-\/o>]{1,2})|(?:<\|--?-?(?:\|>)?)|(?:\.\.?\.?\|?>)/ + }, + { + token: "paren.lparen", + regex: "[:\\[({]" + }, + { + token: "paren.rparen", + regex: "[:\\])}]" + }, + { + caseInsensitive: true + } + ], + "string": [{ + token: "constant.language.escape", + regex: "\\\\" + }, + { + token: "string", + regex: '"', + next: "start" + }, + { + defaultToken: "string" + } + ] + }; + }; + + oop.inherits(PlantUMLHighlightRules, TextHighlightRules); + exports.PlantUmlHighlightRules = PlantUMLHighlightRules; +}); diff --git a/src/plantuml_gui/static/panzoom.min.js b/src/plantuml_gui/static/panzoom.min.js new file mode 100644 index 0000000..017def2 --- /dev/null +++ b/src/plantuml_gui/static/panzoom.min.js @@ -0,0 +1 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.panzoom=f()}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i0){transform.x+=diff;adjusted=true}diff=boundingBox.right-clientRect.left;if(diff<0){transform.x+=diff;adjusted=true}diff=boundingBox.top-clientRect.bottom;if(diff>0){transform.y+=diff;adjusted=true}diff=boundingBox.bottom-clientRect.top;if(diff<0){transform.y+=diff;adjusted=true}return adjusted}function getBoundingBox(){if(!bounds)return;if(typeof bounds==="boolean"){var ownerRect=owner.getBoundingClientRect();var sceneWidth=ownerRect.width;var sceneHeight=ownerRect.height;return{left:sceneWidth*boundsPadding,top:sceneHeight*boundsPadding,right:sceneWidth*(1-boundsPadding),bottom:sceneHeight*(1-boundsPadding)}}return bounds}function getClientRect(){var bbox=panController.getBBox();var leftTop=client(bbox.left,bbox.top);return{left:leftTop.x,top:leftTop.y,right:bbox.width*transform.scale+leftTop.x,bottom:bbox.height*transform.scale+leftTop.y}}function client(x,y){return{x:x*transform.scale+transform.x,y:y*transform.scale+transform.y}}function makeDirty(){isDirty=true;frameAnimation=window.requestAnimationFrame(frame)}function zoomByRatio(clientX,clientY,ratio){if(isNaN(clientX)||isNaN(clientY)||isNaN(ratio)){throw new Error("zoom requires valid numbers")}var newScale=transform.scale*ratio;if(newScalemaxZoom){if(transform.scale===maxZoom)return;ratio=maxZoom/transform.scale}var size=transformToScreen(clientX,clientY);transform.x=size.x-ratio*(size.x-transform.x);transform.y=size.y-ratio*(size.y-transform.y);if(bounds&&boundsPadding===1&&minZoom===1){transform.scale*=ratio;keepTransformInsideBounds()}else{var transformAdjusted=keepTransformInsideBounds();if(!transformAdjusted)transform.scale*=ratio}triggerEvent("zoom");makeDirty()}function zoomAbs(clientX,clientY,zoomLevel){var ratio=zoomLevel/transform.scale;zoomByRatio(clientX,clientY,ratio)}function centerOn(ui){var parent=ui.ownerSVGElement;if(!parent)throw new Error("ui element is required to be within the scene");var clientRect=ui.getBoundingClientRect();var cx=clientRect.left+clientRect.width/2;var cy=clientRect.top+clientRect.height/2;var container=parent.getBoundingClientRect();var dx=container.width/2-cx;var dy=container.height/2-cy;internalMoveBy(dx,dy,true)}function smoothMoveTo(x,y){internalMoveBy(x-transform.x,y-transform.y,true)}function internalMoveBy(dx,dy,smooth){if(!smooth){return moveBy(dx,dy)}if(moveByAnimation)moveByAnimation.cancel();var from={x:0,y:0};var to={x:dx,y:dy};var lastX=0;var lastY=0;moveByAnimation=animate(from,to,{step:function(v){moveBy(v.x-lastX,v.y-lastY);lastX=v.x;lastY=v.y}})}function scroll(x,y){cancelZoomAnimation();moveTo(x,y)}function dispose(){releaseEvents()}function listenForEvents(){owner.addEventListener("mousedown",onMouseDown,{passive:false});owner.addEventListener("dblclick",onDoubleClick,{passive:false});owner.addEventListener("touchstart",onTouch,{passive:false});owner.addEventListener("keydown",onKeyDown,{passive:false});wheel.addWheelListener(owner,onMouseWheel,{passive:false});makeDirty()}function releaseEvents(){wheel.removeWheelListener(owner,onMouseWheel);owner.removeEventListener("mousedown",onMouseDown);owner.removeEventListener("keydown",onKeyDown);owner.removeEventListener("dblclick",onDoubleClick);owner.removeEventListener("touchstart",onTouch);if(frameAnimation){window.cancelAnimationFrame(frameAnimation);frameAnimation=0}smoothScroll.cancel();releaseDocumentMouse();releaseTouches();textSelection.release();triggerPanEnd()}function frame(){if(isDirty)applyTransform()}function applyTransform(){isDirty=false;panController.applyTransform(transform);triggerEvent("transform");frameAnimation=0}function onKeyDown(e){var x=0,y=0,z=0;if(e.keyCode===38){y=1}else if(e.keyCode===40){y=-1}else if(e.keyCode===37){x=1}else if(e.keyCode===39){x=-1}else if(e.keyCode===189||e.keyCode===109){z=1}else if(e.keyCode===187||e.keyCode===107){z=-1}if(filterKey(e,x,y,z)){return}if(x||y){e.preventDefault();e.stopPropagation();var clientRect=owner.getBoundingClientRect();var offset=Math.min(clientRect.width,clientRect.height);var moveSpeedRatio=.05;var dx=offset*moveSpeedRatio*x;var dy=offset*moveSpeedRatio*y;internalMoveBy(dx,dy)}if(z){var scaleMultiplier=getScaleMultiplier(z*100);var offset=transformOrigin?getTransformOriginOffset():midPoint();publicZoomTo(offset.x,offset.y,scaleMultiplier)}}function midPoint(){var ownerRect=owner.getBoundingClientRect();return{x:ownerRect.width/2,y:ownerRect.height/2}}function onTouch(e){beforeTouch(e);if(e.touches.length===1){return handleSingleFingerTouch(e,e.touches[0])}else if(e.touches.length===2){pinchZoomLength=getPinchZoomLength(e.touches[0],e.touches[1]);multiTouch=true;startTouchListenerIfNeeded()}}function beforeTouch(e){if(options.onTouch&&!options.onTouch(e)){return}e.stopPropagation();e.preventDefault()}function beforeDoubleClick(e){if(options.onDoubleClick&&!options.onDoubleClick(e)){return}e.preventDefault();e.stopPropagation()}function handleSingleFingerTouch(e){var touch=e.touches[0];var offset=getOffsetXY(touch);lastSingleFingerOffset=offset;var point=transformToScreen(offset.x,offset.y);mouseX=point.x;mouseY=point.y;smoothScroll.cancel();startTouchListenerIfNeeded()}function startTouchListenerIfNeeded(){if(touchInProgress){return}touchInProgress=true;document.addEventListener("touchmove",handleTouchMove);document.addEventListener("touchend",handleTouchEnd);document.addEventListener("touchcancel",handleTouchEnd)}function handleTouchMove(e){if(e.touches.length===1){e.stopPropagation();var touch=e.touches[0];var offset=getOffsetXY(touch);var point=transformToScreen(offset.x,offset.y);var dx=point.x-mouseX;var dy=point.y-mouseY;if(dx!==0&&dy!==0){triggerPanStart()}mouseX=point.x;mouseY=point.y;internalMoveBy(dx,dy)}else if(e.touches.length===2){multiTouch=true;var t1=e.touches[0];var t2=e.touches[1];var currentPinchLength=getPinchZoomLength(t1,t2);var scaleMultiplier=1+(currentPinchLength/pinchZoomLength-1)*pinchSpeed;var firstTouchPoint=getOffsetXY(t1);var secondTouchPoint=getOffsetXY(t2);mouseX=(firstTouchPoint.x+secondTouchPoint.x)/2;mouseY=(firstTouchPoint.y+secondTouchPoint.y)/2;if(transformOrigin){var offset=getTransformOriginOffset();mouseX=offset.x;mouseY=offset.y}publicZoomTo(mouseX,mouseY,scaleMultiplier);pinchZoomLength=currentPinchLength;e.stopPropagation();e.preventDefault()}}function handleTouchEnd(e){if(e.touches.length>0){var offset=getOffsetXY(e.touches[0]);var point=transformToScreen(offset.x,offset.y);mouseX=point.x;mouseY=point.y}else{var now=new Date;if(now-lastTouchEndTime0)delta*=100;var scaleMultiplier=getScaleMultiplier(delta);if(scaleMultiplier!==1){var offset=transformOrigin?getTransformOriginOffset():getOffsetXY(e);publicZoomTo(offset.x,offset.y,scaleMultiplier);e.preventDefault()}}function getOffsetXY(e){var offsetX,offsetY;var ownerRect=owner.getBoundingClientRect();offsetX=e.clientX-ownerRect.left;offsetY=e.clientY-ownerRect.top;return{x:offsetX,y:offsetY}}function smoothZoom(clientX,clientY,scaleMultiplier){var fromValue=transform.scale;var from={scale:fromValue};var to={scale:scaleMultiplier*fromValue};smoothScroll.cancel();cancelZoomAnimation();zoomToAnimation=animate(from,to,{step:function(v){zoomAbs(clientX,clientY,v.scale)},done:triggerZoomEnd})}function smoothZoomAbs(clientX,clientY,toScaleValue){var fromValue=transform.scale;var from={scale:fromValue};var to={scale:toScaleValue};smoothScroll.cancel();cancelZoomAnimation();zoomToAnimation=animate(from,to,{step:function(v){zoomAbs(clientX,clientY,v.scale)}})}function getTransformOriginOffset(){var ownerRect=owner.getBoundingClientRect();return{x:ownerRect.width*transformOrigin.x,y:ownerRect.height*transformOrigin.y}}function publicZoomTo(clientX,clientY,scaleMultiplier){smoothScroll.cancel();cancelZoomAnimation();return zoomByRatio(clientX,clientY,scaleMultiplier)}function cancelZoomAnimation(){if(zoomToAnimation){zoomToAnimation.cancel();zoomToAnimation=null}}function getScaleMultiplier(delta){var sign=Math.sign(delta);var deltaAdjustedSpeed=Math.min(.25,Math.abs(speed*delta/128));return 1-sign*deltaAdjustedSpeed}function triggerPanStart(){if(!panstartFired){triggerEvent("panstart");panstartFired=true;smoothScroll.start()}}function triggerPanEnd(){if(panstartFired){if(!multiTouch)smoothScroll.stop();triggerEvent("panend")}}function triggerZoomEnd(){triggerEvent("zoomend")}function triggerEvent(name){api.fire(name,api)}}function parseTransformOrigin(options){if(!options)return;if(typeof options==="object"){if(!isNumber(options.x)||!isNumber(options.y))failTransformOrigin(options);return options}failTransformOrigin()}function failTransformOrigin(options){console.error(options);throw new Error(["Cannot parse transform origin.","Some good examples:",' "center center" can be achieved with {x: 0.5, y: 0.5}',' "top center" can be achieved with {x: 0.5, y: 0}',' "bottom right" can be achieved with {x: 1, y: 1}'].join("\n"))}function noop(){}function validateBounds(bounds){var boundsType=typeof bounds;if(boundsType==="undefined"||boundsType==="boolean")return;var validBounds=isNumber(bounds.left)&&isNumber(bounds.top)&&isNumber(bounds.bottom)&&isNumber(bounds.right);if(!validBounds)throw new Error("Bounds object is not valid. It can be: "+"undefined, boolean (true|false) or an object {left, top, right, bottom}")}function isNumber(x){return Number.isFinite(x)}function isNaN(value){if(Number.isNaN){return Number.isNaN(value)}return value!==value}function rigidScroll(){return{start:noop,stop:noop,cancel:noop}}function autoRun(){if(typeof document==="undefined")return;var scripts=document.getElementsByTagName("script");if(!scripts)return;var panzoomScript;for(var i=0;iminVelocity){ax=amplitude*vx;targetX+=ax}if(vy<-minVelocity||vy>minVelocity){ay=amplitude*vy;targetY+=ay}raf=requestAnimationFrame(autoScroll)}function autoScroll(){var elapsed=Date.now()-timestamp;var moving=false;var dx=0;var dy=0;if(ax){dx=-ax*Math.exp(-elapsed/timeConstant);if(dx>.5||dx<-.5)moving=true;else dx=ax=0}if(ay){dy=-ay*Math.exp(-elapsed/timeConstant);if(dy>.5||dy<-.5)moving=true;else dy=ay=0}if(moving){scroll(targetX+dx,targetY+dy);raf=requestAnimationFrame(autoScroll)}}}function getCancelAnimationFrame(){if(typeof cancelAnimationFrame==="function")return cancelAnimationFrame;return clearTimeout}function getRequestAnimationFrame(){if(typeof requestAnimationFrame==="function")return requestAnimationFrame;return function(handler){return setTimeout(handler,16)}}},{}],5:[function(require,module,exports){module.exports=makeSvgController;module.exports.canAttach=isSVGElement;function makeSvgController(svgElement,options){if(!isSVGElement(svgElement)){throw new Error("svg element is required for svg.panzoom to work")}var owner=svgElement.ownerSVGElement;if(!owner){throw new Error("Do not apply panzoom to the root element. "+"Use its child instead (e.g. ). "+"As of March 2016 only FireFox supported transform on the root element")}if(!options.disableKeyboardInteraction){owner.setAttribute("tabindex",0)}var api={getBBox:getBBox,getScreenCTM:getScreenCTM,getOwner:getOwner,applyTransform:applyTransform,initTransform:initTransform};return api;function getOwner(){return owner}function getBBox(){var bbox=svgElement.getBBox();return{left:bbox.x,top:bbox.y,width:bbox.width,height:bbox.height}}function getScreenCTM(){var ctm=owner.getCTM();if(!ctm){return owner.getScreenCTM()}return ctm}function initTransform(transform){var screenCTM=svgElement.getCTM();if(screenCTM===null){screenCTM=document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGMatrix()}transform.x=screenCTM.e;transform.y=screenCTM.f;transform.scale=screenCTM.a;owner.removeAttributeNS(null,"viewBox")}function applyTransform(transform){svgElement.setAttribute("transform","matrix("+transform.scale+" 0 0 "+transform.scale+" "+transform.x+" "+transform.y+")")}}function isSVGElement(element){return element&&element.ownerSVGElement&&element.getCTM}},{}],6:[function(require,module,exports){module.exports=Transform;function Transform(){this.x=0;this.y=0;this.scale=1}},{}],7:[function(require,module,exports){var BezierEasing=require("bezier-easing");var animations={ease:BezierEasing(.25,.1,.25,1),easeIn:BezierEasing(.42,0,1,1),easeOut:BezierEasing(0,0,.58,1),easeInOut:BezierEasing(.42,0,.58,1),linear:BezierEasing(0,0,1,1)};module.exports=animate;module.exports.makeAggregateRaf=makeAggregateRaf;module.exports.sharedScheduler=makeAggregateRaf();function animate(source,target,options){var start=Object.create(null);var diff=Object.create(null);options=options||{};var easing=typeof options.easing==="function"?options.easing:animations[options.easing];if(!easing){if(options.easing){console.warn("Unknown easing function in amator: "+options.easing)}easing=animations.ease}var step=typeof options.step==="function"?options.step:noop;var done=typeof options.done==="function"?options.done:noop;var scheduler=getScheduler(options.scheduler);var keys=Object.keys(target);keys.forEach(function(key){start[key]=source[key];diff[key]=target[key]-source[key]});var durationInMs=typeof options.duration==="number"?options.duration:400;var durationInFrames=Math.max(1,durationInMs*.06);var previousAnimationId;var frame=0;previousAnimationId=scheduler.next(loop);return{cancel:cancel};function cancel(){scheduler.cancel(previousAnimationId);previousAnimationId=0}function loop(){var t=easing(frame/durationInFrames);frame+=1;setValues(t);if(frame<=durationInFrames){previousAnimationId=scheduler.next(loop);step(source)}else{previousAnimationId=0;setTimeout(function(){done(source)},0)}}function setValues(t){keys.forEach(function(key){source[key]=diff[key]*t+start[key]})}}function noop(){}function getScheduler(scheduler){if(!scheduler){var canRaf=typeof window!=="undefined"&&window.requestAnimationFrame;return canRaf?rafScheduler():timeoutScheduler()}if(typeof scheduler.next!=="function")throw new Error("Scheduler is supposed to have next(cb) function");if(typeof scheduler.cancel!=="function")throw new Error("Scheduler is supposed to have cancel(handle) function");return scheduler}function rafScheduler(){return{next:window.requestAnimationFrame.bind(window),cancel:window.cancelAnimationFrame.bind(window)}}function timeoutScheduler(){return{next:function(cb){return setTimeout(cb,1e3/60)},cancel:function(id){return clearTimeout(id)}}}function makeAggregateRaf(){var frontBuffer=new Set;var backBuffer=new Set;var frameToken=0;return{next:next,cancel:next,clearAll:clearAll};function clearAll(){frontBuffer.clear();backBuffer.clear();cancelAnimationFrame(frameToken);frameToken=0}function next(callback){backBuffer.add(callback);renderNextFrame()}function renderNextFrame(){if(!frameToken)frameToken=requestAnimationFrame(renderFrame)}function renderFrame(){frameToken=0;var t=backBuffer;backBuffer=frontBuffer;frontBuffer=t;frontBuffer.forEach(function(callback){callback()});frontBuffer.clear()}function cancel(callback){backBuffer.delete(callback)}}},{"bezier-easing":8}],8:[function(require,module,exports){var NEWTON_ITERATIONS=4;var NEWTON_MIN_SLOPE=.001;var SUBDIVISION_PRECISION=1e-7;var SUBDIVISION_MAX_ITERATIONS=10;var kSplineTableSize=11;var kSampleStepSize=1/(kSplineTableSize-1);var float32ArraySupported=typeof Float32Array==="function";function A(aA1,aA2){return 1-3*aA2+3*aA1}function B(aA1,aA2){return 3*aA2-6*aA1}function C(aA1){return 3*aA1}function calcBezier(aT,aA1,aA2){return((A(aA1,aA2)*aT+B(aA1,aA2))*aT+C(aA1))*aT}function getSlope(aT,aA1,aA2){return 3*A(aA1,aA2)*aT*aT+2*B(aA1,aA2)*aT+C(aA1)}function binarySubdivide(aX,aA,aB,mX1,mX2){var currentX,currentT,i=0;do{currentT=aA+(aB-aA)/2;currentX=calcBezier(currentT,mX1,mX2)-aX;if(currentX>0){aB=currentT}else{aA=currentT}}while(Math.abs(currentX)>SUBDIVISION_PRECISION&&++i=NEWTON_MIN_SLOPE){return newtonRaphsonIterate(aX,guessForT,mX1,mX2)}else if(initialSlope===0){return guessForT}else{return binarySubdivide(aX,intervalStart,intervalStart+kSampleStepSize,mX1,mX2)}}return function BezierEasing(x){if(x===0){return 0}if(x===1){return 1}return calcBezier(getTForX(x),mY1,mY2)}}},{}],9:[function(require,module,exports){module.exports=function eventify(subject){validateSubject(subject);var eventsStorage=createEventsStorage(subject);subject.on=eventsStorage.on;subject.off=eventsStorage.off;subject.fire=eventsStorage.fire;return subject};function createEventsStorage(subject){var registeredEvents=Object.create(null);return{on:function(eventName,callback,ctx){if(typeof callback!=="function"){throw new Error("callback is expected to be a function")}var handlers=registeredEvents[eventName];if(!handlers){handlers=registeredEvents[eventName]=[]}handlers.push({callback:callback,ctx:ctx});return subject},off:function(eventName,callback){var wantToRemoveAll=typeof eventName==="undefined";if(wantToRemoveAll){registeredEvents=Object.create(null);return subject}if(registeredEvents[eventName]){var deleteAllCallbacksForEvent=typeof callback!=="function";if(deleteAllCallbacksForEvent){delete registeredEvents[eventName]}else{var callbacks=registeredEvents[eventName];for(var i=0;i1){fireArguments=Array.prototype.splice.call(arguments,1)}for(var i=0;i { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + + var newname = $('#message-text').val(); + try { + + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("editText", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'newname': newname, + 'oldname': lastclickedsvgelement.textContent, + 'svgelement': lastclickedsvgelement.outerHTML + }), + }); + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + + activitylist = [{ + id: 'addConnectorActivityBelow', + endpoint: 'addToActivity', + arguments: { + type: 'connector' + } + }, { + id: 'addNoteActivity', + endpoint: 'addNoteActivity', + arguments: { + type: 'note' + } + }, { + id: 'addBelowWhile', + endpoint: 'addToActivity', + arguments: { + type: 'while' + } + }, { + id: 'addBelowRepeat', + endpoint: 'addToActivity', + arguments: { + type: 'repeat' + } + }, { + id: 'addBelowFork', + endpoint: 'addToActivity', + arguments: { + type: 'fork' + } + }, { + id: 'addBelowSwitch', + endpoint: 'addToActivity', + arguments: { + type: 'switch' + } + }, { + id: 'addBelowActivity', + endpoint: 'addToActivity', + arguments: { + type: 'activity' + } + }, { + id: 'addIfBelow', + endpoint: 'addToActivity', + arguments: { + type: 'if' + } + }, { + id: 'addStopBelow', + endpoint: 'addToActivity', + arguments: { + type: 'stop' + } + }, { + id: 'addStartBelow', + endpoint: 'addToActivity', + arguments: { + type: 'start' + } + }, { + id: 'addEndBelow', + endpoint: 'addToActivity', + arguments: { + type: 'end' + } + }, { + id: 'detachActivity', + endpoint: 'detachActivity', + arguments: {} + }, { + id: 'breakActivity', + endpoint: 'breakActivity', + arguments: {} + }, { + id: 'delete', + endpoint: 'deleteActivity', + arguments: {} + }, { + id: 'deletebackward', + endpoint: 'deleteActivity', + arguments: {} + }, { + id: 'addArrowLabelAbove', + endpoint: 'addArrowLabel', + arguments: { + where: 'above' + } + }, { + id: 'addArrowLabelBelow', + endpoint: 'addArrowLabel', + arguments: { + where: 'below' + } + }, + + ] + + activitylist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); + + document.getElementById('editactivityinmenu').addEventListener('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + try { + + const response = await fetch("getText", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + $('#message-text').val(await response.text()); + $('#modalForm').modal('show'); + $('#modalForm').on('shown.bs.modal', function() { + $('#message-text').trigger('focus') + }) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + document.getElementById('editactivityinmenubackward').addEventListener('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + try { + + const response = await fetch("getText", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + $('#message-text').val(await response.text()); + $('#modalForm').modal('show'); + $('#modalForm').on('shown.bs.modal', function() { + $('#message-text').trigger('focus') + }) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + +} + +function ifEventListeners() { + $('#submitif').on('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + + var statement = $('#statement').val(); + var branch1 = $('#branch1').val(); + var branch2 = $('#branch2').val(); + try { + + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("editTextIf", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'statement': statement, + 'branch1': branch1, + 'branch2': branch2, + 'svgelement': lastclickedsvgelement.outerHTML + }), + }); + + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + + document.getElementById('editiftextmenu').addEventListener('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + const pumlcontent = trimlines(editor.session.getValue()); + try { + + const response = await fetch("getTextPoly", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + const texts = await response.json(); + + $('#statement').val(texts[0]); + $('#branch1').val(texts[1]); + $('#branch2').val(texts[2]); + $('#modalFormif').modal('show'); + $('#modalFormif').on('shown.bs.modal', function() { + $('#statement').trigger('focus') + }) + + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + document.getElementById('editiftextmenurepeat').addEventListener('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + const pumlcontent = trimlines(editor.session.getValue()); + try { + + const response = await fetch("getTextPoly", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + const texts = await response.json(); + + $('#statement').val(texts[0]); + $('#branch1').val(texts[1]); + $('#branch2').val(texts[2]); + $('#modalFormif').modal('show'); + $('#modalFormif').on('shown.bs.modal', function() { + $('#statement').trigger('focus') + }) + + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + + const iflist = [{ + id: 'detachIf', + endpoint: 'detachIf', + arguments: {} + }, { + id: 'delIf', + endpoint: 'delIf', + arguments: {} + }, { + id: 'delIfrepeat', + endpoint: 'delIf', + arguments: {} + }, { + id: 'addbackwards', + endpoint: 'addBackwards', + arguments: {} + }, { + id: 'addleft', + endpoint: 'addToIf', + arguments: { + where: 'left', + type: 'activity' + } + }, { + id: 'addright', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'activity' + } + }, { + id: 'addactivityrightrepeat', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'activity' + } + }, { + id: 'addleftif', + endpoint: 'addToIf', + arguments: { + where: 'left', + type: 'if' + } + }, { + id: 'addrightif', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'if' + } + }, { + id: 'addrightifrepeat', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'if' + } + }, { + id: 'addrightforkrepeat', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'fork' + } + }, { + id: 'addrightswitchrepeat', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'switch' + } + }, { + id: 'addrightfork', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'fork' + } + }, { + id: 'addleftfork', + endpoint: 'addToIf', + arguments: { + where: 'left', + type: 'fork' + } + }, { + id: 'addrightswitch', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'switch' + } + }, { + id: 'addleftswitch', + endpoint: 'addToIf', + arguments: { + where: 'left', + type: 'switch' + } + }, { + id: 'addrightwhile', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'while' + } + }, { + id: 'addrightrepeat', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'repeat' + } + }, { + id: 'addrightwhilerepeat', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'while' + } + }, { + id: 'addleftwhile', + endpoint: 'addToIf', + arguments: { + where: 'left', + type: 'while' + } + }, { + id: 'addleftrepeat', + endpoint: 'addToIf', + arguments: { + where: 'left', + type: 'repeat' + } + }, { + id: 'addrightconnectorrepeat', + endpoint: 'addToIf', + arguments: { + where: 'right', + type: 'connector' + } + }]; + + iflist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); + + +} + +function ellipseEventListeners() { + const ellipselist = [{ + id: 'addwhilebelowellipse', + endpoint: 'addToEllipse', + arguments: { + where: 'below', + type: 'while' + } + }, { + id: 'addrepeatbelowellipse', + endpoint: 'addToEllipse', + arguments: { + where: 'below', + type: 'repeat' + } + }, { + id: 'addconnectorbelowellipse', + endpoint: 'addToEllipse', + arguments: { + where: 'below', + type: 'connector' + } + }, { + id: 'addforkbelowellipse', + endpoint: 'addToEllipse', + arguments: { + where: 'below', + type: 'fork' + } + }, { + id: 'addswitchbelowellipse', + endpoint: 'addToEllipse', + arguments: { + where: 'below', + type: 'switch' + } + }, { + id: 'ellipsedelete', + endpoint: 'deleteEllipse', + arguments: {} + }, { + id: 'addactivitybelowellipse', + endpoint: 'addToEllipse', + arguments: { + where: 'below', + type: 'activity' + } + }, { + id: 'addifbelowellipse', + endpoint: 'addToEllipse', + arguments: { + where: 'below', + type: 'if' + } + }]; + + ellipselist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); +} + +function setDemo() { + puml = `@startuml +title +This is not an ordinary PlantUML editor! +Try double-click me to change the title! +endtitle +start +#lightyellow:Right-click to open the menu and choose +"Add Below" to add new activities below; +if (Right-click me to add elements\\nto the different branches) then (yes) +:To delete this Activity, either delete all the text +or choose "Delete" in the menu!; +switch (Right-click me\\nand choose "switch again"\\nto add another process) +case ( condition 1) +:Activity; +case ( Double-click me to edit the text!) +:Activity; +endswitch +#red:Activity; +(C) +else (no) +:To edit the activity either double-click +or press "Edit Text" in the menu; +group group +if (Statement) then (yes) +:Activity; +else (no) +#lightgreen:[[https://ericsson.com Link to Ericsson]] Try clicking the +link inside this activity!; +fork +:To add an arrow-label to +this activity, open the menu!; +stop +fork again +:Try deleting me and +pressing CTRL + Z to undo!; +fork again +:action; +note right +You can press CTRL + ENTER +to submit text when editing! +end note +end merge +endif +end group +endif +detach +:To add a note choose "Add Note" in the menu; +detach +(C) +:Activity; +note right +Try toggling the side of the +note by using the menu! +end note +stop +@enduml`; + setPuml(puml) +} + +function buttonEventListeners() { + + document.getElementById('demo').addEventListener('click', function() { + setDemo() + }); + + + document.getElementById('clear').addEventListener('click', function() { + puml = "@startuml\nstart\n@enduml" + setPuml(puml) + }); + + document.getElementById('undo').addEventListener('click', function() { + undoeditor(); + + }); + + document.getElementById('restore').addEventListener('click', function() { + restoreeditor(); + + }); + + document.getElementById('addTitleButton').addEventListener('click', async () => { + try { + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("addTitle", { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml + }) + }); + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + + document.getElementById('png').addEventListener('click', async () => { + try { + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("/renderPNG", { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml + }) + }); + + const blob = await response.blob(); + const imageUrl = URL.createObjectURL(blob); + const newTab = window.open('', '_blank'); + + const img = newTab.document.createElement('img'); + img.src = imageUrl; + newTab.document.body.appendChild(img); + newTab.document.body.style.textAlign = 'center'; + newTab.document.close(); + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + + + + + + document.getElementById('copybutton').addEventListener('click', function() { + navigator.clipboard.writeText(editor.session.getValue()) + + }); + + + document.getElementById('pastebutton').addEventListener('click', function() { + navigator.clipboard.readText().then(function(text) { + setPuml(text) + }).catch(function(err) { + console.error('Failed to read clipboard contents: ', err); + }); + }); + +} + +function forkEventListeners() { + + const forklist = [{ + id: 'deleteFork', + endpoint: 'deleteFork', + arguments: {} + }, { + id: 'forkAgain', + endpoint: 'forkAgain', + arguments: {} + }, { + id: 'forkToggle', + endpoint: 'forkToggle', + arguments: {} + }, ]; + + forklist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); + +} + +function switchEventListeners() { + + const forklist = [{ + id: 'delIfswitch', + endpoint: 'delIf', + arguments: {} + }, { + id: 'switchagain', + endpoint: 'switchAgain', + arguments: {} + }]; + + forklist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); + + document.getElementById('editiftextmenuswitch').addEventListener('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + const pumlcontent = trimlines(editor.session.getValue()); + try { + + const response = await fetch("getTextPoly", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + const texts = await response.json(); + + $('#switch-text').val(texts[0]); + $('#modalFormswitch').modal('show'); + $('#modalFormswitch').on('shown.bs.modal', function() { + $('#switch-text').trigger('focus') + }) + + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + $('#submitswitch').on('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + + var statement = $('#switch-text').val(); + try { + + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("editTextIf", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'statement': statement, + 'branch1': "", + 'branch2': "", + 'svgelement': lastclickedsvgelement.outerHTML + }), + }); + + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + +} + + +function bottomForkEventListeners() { + const forklist = [{ + id: 'forkbottomtoggle', + endpoint: 'forkToggle2', + line: fline, + arguments: {} + }, { + id: 'deletefork2', + endpoint: 'deleteFork2', + line: fline, + arguments: {} + }, { + id: 'activityfork', + endpoint: 'addToFork', + line: fline, + arguments: { + type: 'activity' + } + }, { + id: 'iffork', + endpoint: 'addToFork', + line: fline, + arguments: { + type: 'if' + } + }, { + id: 'forkfork', + endpoint: 'addToFork', + line: fline, + arguments: { + type: 'fork' + } + }, { + id: 'switchfork', + endpoint: 'addToFork', + line: fline, + arguments: { + type: 'switch' + } + }, { + id: 'whilefork', + endpoint: 'addToFork', + line: fline, + arguments: { + type: 'while' + } + }, { + id: 'repeatfork', + endpoint: 'addToFork', + line: fline, + arguments: { + type: 'repeat' + } + }, { + id: 'connectorfork', + endpoint: 'addToFork', + line: fline, + arguments: { + type: 'connector' + } + }, { + id: 'startfork', + endpoint: 'addToFork', + line: fline, + arguments: { + type: 'start' + } + }, { + id: 'stopfork', + endpoint: 'addToFork', + line: fline, + arguments: { + type: 'stop' + } + }, { + id: 'endfork', + endpoint: 'addToFork', + line: fline, + arguments: { + type: 'end' + } + }]; + + + forklist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + 'line': Number(lastclickedsvgelement.getAttribute('fline')), + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); +} + +function titleEventListeners() { + $('#submit-title').on('click', async () => { + var text = $('#title-text').val(); + try { + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("editTitle", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'title': text + }), + }); + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + + document.getElementById('editTitle').addEventListener('click', async () => { + try { + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("getTextTitle", { + + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + }) + }); + const text = await response.text(); + + $('#title-text').val(text); + $('#modalFormTitle').modal('show'); + $('#modalFormTitle').on('shown.bs.modal', function() { + $('#title-text').trigger('focus') + }) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + }); + + document.getElementById('deleteTitle').addEventListener('click', async () => { + try { + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("deleteTitle", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + }), + }); + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + +} + +function noteEventListeners() { + $('#submit-note').on('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + var text = $('#note-text').val(); + try { + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("editNote", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + 'text': text + }), + }); + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + + const notelist = [{ + id: 'deleteNote', + endpoint: 'deleteNote', + arguments: {} + }, { + id: 'noteToggle', + endpoint: 'noteToggle', + arguments: {} + }, ]; + + notelist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); + + document.getElementById('noteEdit').addEventListener('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + + const response = await fetch("getNoteText", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + $('#note-text').val(await response.text()); + $('#modalFormNote').modal('show'); + $('#modalFormNote').on('shown.bs.modal', function() { + $('#note-text').trigger('focus') + }) + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + }) +} + +function groupEventListeners() { + + $('#submit-group').on('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + var text = $('#group-text').val(); + try { + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("editGroup", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + 'text': text + }), + }); + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + + + document.getElementById('deleteGroup').addEventListener('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + + try { + + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("deleteGroup", { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + + document.getElementById('groupEdit').addEventListener('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + pumlcontent = trimlines(editor.session.getValue()); + try { + + const response = await fetch("getGroupText", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + $('#group-text').val(await response.text()); + $('#modalFormGroup').modal('show'); + $('#modalFormGroup').on('shown.bs.modal', function() { + $('#group-text').trigger('focus') + }) + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) +} + +function mergeEventListeners() { + const mergelist = [{ + id: 'addwhilemerge', + endpoint: 'addToMerge', + arguments: { + type: 'while' + } + }, { + id: 'addrepeatmerge', + endpoint: 'addToMerge', + arguments: { + type: 'repeat' + } + }, { + id: 'addactivitymerge', + endpoint: 'addToMerge', + arguments: { + type: 'activity' + } + }, { + id: 'addifmerge', + endpoint: 'addToMerge', + arguments: { + type: 'if' + } + }, { + id: 'addforkmerge', + endpoint: 'addToMerge', + arguments: { + type: 'fork' + } + }, { + id: 'addswitchmerge', + endpoint: 'addToMerge', + arguments: { + type: 'switch' + } + }, { + id: 'addstartmerge', + endpoint: 'addToMerge', + arguments: { + type: 'start' + } + }, { + id: 'addstopmerge', + endpoint: 'addToMerge', + arguments: { + type: 'stop' + } + }, { + id: 'addendmerge', + endpoint: 'addToMerge', + arguments: { + type: 'end' + } + }]; + + mergelist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); + + + +} + +function whileEventListeners() { + $('#submitwhile').on('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + + var statement = $('#whilestatement').val(); + var branch1 = $('#break').val(); + var branch2 = $('#loop').val(); + try { + + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("editTextWhile", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'whilestatement': statement, + 'break': branch1, + 'loop': branch2, + 'svgelement': lastclickedsvgelement.outerHTML + }), + }); + + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + + + const whilelist = [{ + id: 'delwhile', + endpoint: 'delWhile', + arguments: {} + }, { + id: 'addactivityloop', + endpoint: 'addToWhile', + arguments: { + type: 'activity', + where: 'loop' + } + }, { + id: 'addactivitybreak', + endpoint: 'addToWhile', + arguments: { + type: 'activity', + where: 'break' + } + }, { + id: 'addifloop', + endpoint: 'addToWhile', + arguments: { + type: 'if', + where: 'loop' + } + }, { + id: 'addifbreak', + endpoint: 'addToWhile', + arguments: { + type: 'if', + where: 'break' + } + }, { + id: 'addforkloop', + endpoint: 'addToWhile', + arguments: { + type: 'fork', + where: 'loop' + } + }, { + id: 'addforkbreak', + endpoint: 'addToWhile', + arguments: { + type: 'fork', + where: 'break' + } + }, { + id: 'addswitchloop', + endpoint: 'addToWhile', + arguments: { + type: 'switch', + where: 'loop' + } + }, { + id: 'addswitchbreak', + endpoint: 'addToWhile', + arguments: { + type: 'switch', + where: 'break' + } + }, { + id: 'addwhileloop', + endpoint: 'addToWhile', + arguments: { + type: 'while', + where: 'loop' + } + }, { + id: 'addrepeatloop', + endpoint: 'addToWhile', + arguments: { + type: 'repeat', + where: 'loop' + } + }, { + id: 'addwhilebreak', + endpoint: 'addToWhile', + arguments: { + type: 'while', + where: 'break' + } + }, { + id: 'addrepeatbreak', + endpoint: 'addToWhile', + arguments: { + type: 'repeat', + where: 'break' + } + }, { + id: 'addstartloop', + endpoint: 'addToWhile', + arguments: { + type: 'start', + where: 'loop' + } + }, { + id: 'addendloop', + endpoint: 'addToWhile', + arguments: { + type: 'end', + where: 'loop' + } + }, { + id: 'addstoploop', + endpoint: 'addToWhile', + arguments: { + type: 'stop', + where: 'loop' + } + }, { + id: 'addstartbreak', + endpoint: 'addToWhile', + arguments: { + type: 'start', + where: 'break' + } + }, { + id: 'addendbreak', + endpoint: 'addToWhile', + arguments: { + type: 'end', + where: 'break' + } + }, { + id: 'addstopbreak', + endpoint: 'addToWhile', + arguments: { + type: 'stop', + where: 'break' + } + }]; + + + whilelist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); + + + + + + document.getElementById('editwhilemenu').addEventListener('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + try { + + const response = await fetch("getTextWhile", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + const texts = await response.json(); + + $('#whilestatement').val(texts[0]); + $('#break').val(texts[1]); + $('#loop').val(texts[2]); + $('#modalFormWhile').modal('show'); + $('#modalFormWhile').on('shown.bs.modal', function() { + $('#whilestatement').trigger('focus') + }) + + + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + +} + +function connectorEventListeners() { + $('#submit-connector').on('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + var text = $('#connector-text').val(); + try { + + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("editCharConnector", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'text': text, + 'svgelement': lastclickedsvgelement.outerHTML + }), + }); + + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + + const connectorlist = [{ + id: 'connectordelete', + endpoint: 'connectorDelete', + arguments: {} + }, { + id: 'toggledetachconnector', + endpoint: 'detachConnector', + arguments: {} + }, { + id: 'noteconnector', + endpoint: 'addToConnector', + arguments: { + where: 'below', + type: 'note' + } + }, { + id: 'addactivitybelowconnector', + endpoint: 'addToConnector', + arguments: { + where: 'below', + type: 'activity' + } + }, { + id: 'addifbelowconnector', + endpoint: 'addToConnector', + arguments: { + where: 'below', + type: 'if' + } + }, { + id: 'addwhilebelowconnector', + endpoint: 'addToConnector', + arguments: { + where: 'below', + type: 'while' + } + }, { + id: 'addrepeatbelowconnector', + endpoint: 'addToConnector', + arguments: { + where: 'below', + type: 'repeat' + } + }, { + id: 'addforkbelowconnector', + endpoint: 'addToConnector', + arguments: { + where: 'below', + type: 'fork' + } + }, { + id: 'addswitchbelowconnector', + endpoint: 'addToConnector', + arguments: { + where: 'below', + type: 'switch' + } + }]; + + connectorlist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); + + +} + +function arrowLabelEventListeners() { + const arrowlist = [{ + id: 'arrowlabeldelete', + endpoint: 'delArrow', + arguments: {} + }, ]; + + + arrowlist.forEach(item => { + document.getElementById(item.id).addEventListener('click', async () => { + const element = document.getElementById('colb'); + const svg = element.querySelector('g'); + try { + + const plantuml = trimlines(editor.session.getValue()); + const toBeStringified = { + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML, + } + if (item.arguments) { + for (let [key, value] of Object.entries(item.arguments)) { + toBeStringified[key] = value; + } + } + const response = await fetch(item.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(toBeStringified) + }); + const pumlcontentcode = await response.text(); + + setPuml(pumlcontentcode); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + }); + + $('#submit-arrow').on('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + var text = $('#arrow-text').val(); + try { + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("editArrow", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml, + 'svg': svg.innerHTML, + 'text': text, + 'svgelement': lastclickedsvgelement.outerHTML + }), + }); + const pumlcontentcode = await response.text() + setPuml(pumlcontentcode) + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }) + + document.getElementById('arrowlabeledit').addEventListener('click', async () => { + const element = document.getElementById('colb') + const svg = element.querySelector('g'); + try { + // First fetch to check for duplicates + const checkDuplicateResponse = await fetch("checkDuplicateArrow", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': editor.session.getValue(), + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + + const checkDuplicateData = await checkDuplicateResponse.json(); + const isDuplicate = checkDuplicateData.result; + const arrowType = checkDuplicateData.type + + // Only fetch ArrowText if the duplicate check returns false + if (!isDuplicate) { + try { + const arrowTextResponse = await fetch("getArrowText", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + + const arrowText = await arrowTextResponse.text(); + $('#arrow-text').val(arrowText); + $('#modalFormArrow').modal('show'); + $('#modalFormArrow').on('shown.bs.modal', function() { + $('#arrow-text').trigger('focus'); + }); + } catch (error) { + console.error('Error with fetch API when fetching ArrowText', error); + } + } else if (arrowType == "arrow") { + $('#duplicateArrowModal').modal('show'); + } else { + $('#duplicateCaseModal').modal('show'); + } + } catch (error) { + console.error('Error with fetch API during duplicate check', error); + } + }); + + +} + +function commandEventListeners() { + document.addEventListener('keydown', function(event) { + if (event.ctrlKey && event.key === "z") { + console.log("hej") + event.preventDefault(); + undoeditor(); + } + + if (event.ctrlKey && event.key === "y") { + event.preventDefault(); + restoreeditor(); + } + + + if (event.ctrlKey && event.key === 'Enter') { + event.preventDefault(); + + var activityModal = document.getElementById('modalForm'); + if ($(activityModal).hasClass('show')) { + document.getElementById('submit').click(); + } + + var arrowModal = document.getElementById('modalFormArrow'); + if ($(arrowModal).hasClass('show')) { + document.getElementById('submit-arrow').click(); + } + var ifModal = document.getElementById('modalFormif'); + if ($(ifModal).hasClass('show')) { + document.getElementById('submitif').click(); + } + + var switchModal = document.getElementById('modalFormswitch'); + if ($(switchModal).hasClass('show')) { + document.getElementById('submitswitch').click(); + } + + var titleModal = document.getElementById('modalFormTitle'); + if ($(titleModal).hasClass('show')) { + document.getElementById('submit-title').click(); + } + + var noteModal = document.getElementById('modalFormNote'); + if ($(noteModal).hasClass('show')) { + document.getElementById('submit-note').click(); + } + + var groupModal = document.getElementById('modalFormGroup'); + if ($(groupModal).hasClass('show')) { + document.getElementById('submit-group').click(); + } + + var whileModal = document.getElementById('modalFormWhile'); + if ($(whileModal).hasClass('show')) { + document.getElementById('submitwhile').click(); + } + + var connectorModal = document.getElementById('modalFormConnector'); + if ($(connectorModal).hasClass('show')) { + document.getElementById('submit-connector').click(); + } + } + }); +} + +function addEventListeners() { + activityEventListeners(); + ifEventListeners(); + ellipseEventListeners(); + buttonEventListeners(); + titleEventListeners(); + forkEventListeners(); + noteEventListeners(); + groupEventListeners(); + mergeEventListeners(); + whileEventListeners(); + connectorEventListeners(); + bottomForkEventListeners(); + arrowLabelEventListeners(); + commandEventListeners(); + switchEventListeners(); + + // turn of menus when clicking anywhere. + document.addEventListener('click', function(e) { + var menuIds = [ + 'activity-menu', + 'ellipse-menu', + 'if-menu', + 'fork-menu', + 'note-menu', + 'group-menu', + 'merge-menu', + 'while-menu', + 'connector-menu', + 'arrowlabel-menu', + 'repeat-menu', + 'backward-menu', + 'forkbottom-menu', + 'switch-menu', + 'title-menu' + ]; + + menuIds.forEach(function(id) { + var menu = document.getElementById(id); + if (menu) { + menu.style.display = 'none'; + } + }); + }); + +} + +function saveToHistory(puml) { + if (puml === "") { + return; // Avoid saving empty strings + } + + if (historyPointer >= 0 && history[historyPointer] === puml) { + return + } + + history = history.slice(0, historyPointer + 1); + history.push(puml); + historyPointer++ +} + +function debounce(func, wait) { + let timeout; + return function(...args) { + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(this, args), wait); + }; +} + +const debouncedRenderPlantUml = debounce(renderPlantUml, 200); + +async function fetchSvgFromPlantUml() { + try { + const plantuml = editor.session.getValue(); + const response = await fetch("render", { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml + }) + }); + const svg = await response.text() + return svg + } catch (error) { + console.error('Error with render fetch api?', error); + } +} + +function toggleLoadingOverlay() { + const overlay = document.getElementById('loading-overlay'); + if (overlay.style.display === 'none' || overlay.style.display === '') { + overlay.style.display = 'block'; + } else { + overlay.style.display = 'none'; + } +} + + + +async function renderPlantUml() { + if (document.getElementById('popup').style.visibility = "visible") { + document.getElementById('popup').style.visibility = "hidden"; // Hide the error popup when rendering again. + } + toggleLoadingOverlay(); + const element = document.getElementById('colb') + const pumlcontent = trimlines(editor.session.getValue()); + saveToHistory(pumlcontent); + try { + const plantuml = trimlines(editor.session.getValue()); + const response = await fetch("encode", { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': plantuml + }) + }); + const res = await response.text() + var url = window.location.href; + var x = url.indexOf("?"); + if (x == -1) + url = url + "?" + res; + else + url = url.substr(0, x) + "?" + res; + window.history.replaceState(null, '', url); + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + + fetchSvgFromPlantUml().then((svgContent) => { + element.innerHTML = svgContent; + const svg = element.querySelector('g'); + if (!svg) { + toggleLoadingOverlay() + return + } + const svgelements = svg.querySelectorAll('*'); + + let onlytextelements = true + forkqueue = labelForks(pumlcontent) + + for (let index = 0; index < svgelements.length;) { + let svgelement = svgelements[index] + if (svgelement.tagName.toLowerCase() != 'text') { + onlytextelements = false + } + if (svgelement.tagName.toLowerCase() === 'line') { + svgelement.style.pointerEvents = 'none'; + } + if (checkIfActivity(svgelements, index)) { + svgelement.addEventListener('dblclick', async () => { + lastclickedsvgelement = svgelement; + try { + + const response = await fetch("getText", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + $('#message-text').val(await response.text()); + $('#modalForm').modal('show'); + $('#modalForm').on('shown.bs.modal', function() { + $('#message-text').trigger('focus') + }) + + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + handleContextMenuActivity(pumlcontent, svg, svgelement); + + let rectcolor = "" + svgelement.addEventListener('mouseover', function() { + const svg = element.querySelector('g'); + resetHighlight(svg); + + processActivityLine(pumlcontent, svg, svgelement) + rectcolor = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#d8d8d8') + }); + + svgelement.addEventListener('mouseout', function() { + svgelement.setAttribute('fill', rectcolor) + }); + } + + if (checkIfFork(svgelements, index)) { + let forkobj = forkqueue.shift(); + svgelement.setAttribute('fline', forkobj.index) + if (forkobj.line == "top") { + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement + e.preventDefault(); + var contextMenu = document.getElementById('fork-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + } else { + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement + e.preventDefault(); + var contextMenu = document.getElementById('forkbottom-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + } + let rectcolor = "" + svgelement.addEventListener('mouseover', function() { + getmarkersinglelines(parseInt(svgelement.getAttribute('fline'), 10)) + rectcolor = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#d8d8d8') + }); + + svgelement.addEventListener('mouseout', function() { + svgelement.setAttribute('fill', rectcolor) + }); + } + + if (checkIfWhile(svgelements, index)) { + svgelement.addEventListener('dblclick', async () => { + lastclickedsvgelement = svgelement; + try { + + const response = await fetch("getTextWhile", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + const texts = await response.json(); + + $('#whilestatement').val(texts[0]); + $('#break').val(texts[1]); + $('#loop').val(texts[2]); + $('#modalFormWhile').modal('show'); + $('#modalFormWhile').on('shown.bs.modal', function() { + $('#whilestatement').trigger('focus') + }) + + + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement + e.preventDefault(); + var contextMenu = document.getElementById('while-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + + let color = "" + svgelement.addEventListener('mouseover', function() { + processWhileLine(pumlcontent, svg, svgelement) + color = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#d8d8d8') + }); + + svgelement.addEventListener('mouseout', function() { + svgelement.setAttribute('fill', color) + }); + + } + + if (checkIfCorrectPoly(svgelements, index) && !checkIfWhile(svgelements, index) && !checkIfMergePoly(svgelements, index)) { // checks if its an actual if polygon with text or and endif + svgelement.addEventListener('dblclick', async () => { + lastclickedsvgelement = svgelement; + try { + + const response = await fetch("getTextPoly", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + const texts = await response.json(); + const isSwitch = await checkSwitch(pumlcontent, svg, lastclickedsvgelement) + if (isSwitch) { + $('#switch-text').val(texts[0]); + $('#modalFormswitch').modal('show'); + $('#modalFormswitch').on('shown.bs.modal', function() { + $('#switch-text').trigger('focus') + }) + } else { + $('#statement').val(texts[0]); + $('#branch1').val(texts[1]); + $('#branch2').val(texts[2]); + $('#modalFormif').modal('show'); + $('#modalFormif').on('shown.bs.modal', function() { + $('#statement').trigger('focus') + }) + } + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + handleContextMenuPoly(pumlcontent, svg, svgelement) // adds the correct context menu depending on if its an if or repeat + + + let polycolor = "" + svgelement.addEventListener('mouseover', function() { + processIfLine(pumlcontent, svg, svgelement) + polycolor = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#d8d8d8') + }); + + svgelement.addEventListener('mouseout', function() { + svgelement.setAttribute('fill', polycolor) + }); + } + + if (checkIfNote(svgelements, index)) { + svgelement.addEventListener('dblclick', async () => { + lastclickedsvgelement = svgelement; + try { + + const response = await fetch("getNoteText", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + $('#note-text').val(await response.text()); + $('#modalFormNote').modal('show'); + $('#modalFormNote').on('shown.bs.modal', function() { + $('#note-text').trigger('focus') + }) + + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement + e.preventDefault(); + var contextMenu = document.getElementById('note-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + + let rectcolor = "" + svgelement.addEventListener('mouseover', function() { + processNoteLine(pumlcontent, svg, svgelement) + rectcolor = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#d8d8d8') + }); + + svgelement.addEventListener('mouseout', function() { + svgelement.setAttribute('fill', rectcolor) + }); + } + + if (checkIfMergePoly(svgelements, index)) { + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement + e.preventDefault() + var contextMenu = document.getElementById('merge-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + + let rectcolor = "" + svgelement.addEventListener('mouseover', function() { + processMergeLine(pumlcontent, svg, svgelement) + rectcolor = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#d8d8d8') + }); + + svgelement.addEventListener('mouseout', function() { + svgelement.setAttribute('fill', rectcolor) + }); + + } + + if (checkIfGroup(svgelements, index)) { + //svgelement.setAttribute('fill', 'transparent') // on click works poorly if fill is 'none' + svgelement.addEventListener('dblclick', async () => { + lastclickedsvgelement = svgelements[index - 2]; + try { + + + const response = await fetch("getGroupText", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + $('#group-text').val(await response.text()); + $('#modalFormGroup').modal('show'); + $('#modalFormGroup').on('shown.bs.modal', function() { + $('#group-text').trigger('focus') + }) + + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelements[index - 2]; + e.preventDefault(); + var contextMenu = document.getElementById('group-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + + let rectcolor = "" + svgelement.addEventListener('mouseenter', function() { + processGroupLine(pumlcontent, svg, svgelements[index - 2]) + rectcolor = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#d8d8d8') + }); + + svgelement.addEventListener('mouseleave', function() { + svgelement.setAttribute('fill', rectcolor) + }); + } + + + if (checkIfEllipse(svgelements, index)) { + if (svgelement.getAttribute('fill') === 'none') { + svgelement.setAttribute('fill', 'transparent'); // changes background from none to make it clickable + } + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement + e.preventDefault(); + var contextMenu = document.getElementById('ellipse-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + + let ellipsecolor = "" + svgelement.addEventListener('mouseover', function() { + processEllipseLine(pumlcontent, svg, svgelement) + ellipsecolor = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#818181 ') + }); + + svgelement.addEventListener('mouseout', function() { + svgelement.setAttribute('fill', ellipsecolor) + }); + + } + + if (checkIfConnector(svgelements, index)) { + svgelement.addEventListener('dblclick', async () => { + lastclickedsvgelement = svgelement; + try { + const response = await fetch("getCharConnector", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + $('#connector-text').val(await response.text()); + $('#modalFormConnector').modal('show'); + $('#modalFormConnector').on('shown.bs.modal', function() { + $('#connector-text').trigger('focus') + }) + + + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + }); + + + svgelements[index + 1].style.pointerEvents = 'none'; + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement + e.preventDefault(); + var contextMenu = document.getElementById('connector-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + + let ellipsecolor = "" + svgelement.addEventListener('mouseover', function() { + processConnectorLine(pumlcontent, svg, svgelement) + ellipsecolor = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#c2c2c2') + }); + + svgelement.addEventListener('mouseout', function() { + svgelement.setAttribute('fill', ellipsecolor) + }); + + } + + if (checkIfArrowLabel(svgelements, index)) { + let arrow = svgelements[index - 1] + while (index < svgelements.length && svgelements[index].tagName.toLowerCase() === 'text') { + svgelements[index].addEventListener('dblclick', async () => { + lastclickedsvgelement = arrow; + + try { + // First fetch to check for duplicates + const checkDuplicateResponse = await fetch("checkDuplicateArrow", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': editor.session.getValue(), + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + + const checkDuplicateData = await checkDuplicateResponse.json(); + const isDuplicate = checkDuplicateData.result; + const arrowType = checkDuplicateData.type + + // Only fetch ArrowText if the duplicate check returns false + if (!isDuplicate) { + try { + const arrowTextResponse = await fetch("getArrowText", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'svg': svg.innerHTML, + 'svgelement': lastclickedsvgelement.outerHTML + }) + }); + + const arrowText = await arrowTextResponse.text(); + $('#arrow-text').val(arrowText); + $('#modalFormArrow').modal('show'); + $('#modalFormArrow').on('shown.bs.modal', function() { + $('#arrow-text').trigger('focus'); + }); + } catch (error) { + console.error('Error with fetch API when fetching ArrowText', error); + } + } else if (arrowType == "arrow") { + $('#duplicateArrowModal').modal('show'); + } else { + $('#duplicateCaseModal').modal('show'); + } + } catch (error) { + console.error('Error with fetch API during duplicate check', error); + } + }); + + svgelements[index].addEventListener('contextmenu', function(e) { + lastclickedsvgelement = arrow + e.preventDefault(); + var contextMenu = document.getElementById('arrowlabel-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + + let rectcolor = "" + let svgelement = svgelements[index] + svgelement.addEventListener('mouseenter', function() { + processArrowLine(pumlcontent, svg, arrow) + rectcolor = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#d8d8d8') + }); + + svgelement.addEventListener('mouseleave', function() { + svgelement.setAttribute('fill', rectcolor) + }); + + index++ + } + } + + if ( + !onlytextelements && + svgelements[index] && + (svgelements[index].tagName.toLowerCase() === 'text') + ) { + const previousElement = svgelements[index].parentElement; + if ( + (!previousElement || previousElement.tagName.toLowerCase() !== 'a') && + (svgelements[index - 1].getAttribute('style') !== "stroke:#000000;stroke-width:1.5;") && + (svgelements[index - 1].getAttribute('style') !== "stroke:#181818;stroke-width:1.0;") + ) { + // We remove the pointer event for text elements unless its an arrow label, clickable link, group label or the title + svgelements[index].style.pointerEvents = 'none'; + } + } + + if (svgelements[index] && svgelements[index].tagName.toLowerCase() === 'a') { + svgelements[index].setAttribute('target', '_blank') + } + + + if (checkIfTitleRect(svgelements, index)) { + svgelement.setAttribute('fill', 'transparent') + svgelement.setAttribute('style', '"stroke:#00000000;stroke-width:1.0;fill:transparent;"') + + svgelement.addEventListener('dblclick', async () => { + try { + + const response = await fetch("getTextTitle", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + const text = await response.text(); + + $('#title-text').val(text); + $('#modalFormTitle').modal('show'); + $('#modalFormTitle').on('shown.bs.modal', function() { + $('#title-text').trigger('focus') + }) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + + }); + + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement + e.preventDefault(); + var contextMenu = document.getElementById('title-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + + + let rectcolor = "" + svgelement.addEventListener('mouseenter', function() { + processTitleLine(pumlcontent) + rectcolor = svgelement.getAttribute('fill') + svgelement.setAttribute('fill', '#e5e5e5') + }); + + svgelement.addEventListener('mouseleave', function() { + svgelement.setAttribute('fill', rectcolor) + }); + + } + index++ + } + toggleLoadingOverlay() + + }).catch((error) => { + displayErrorMessage(`Error rendering SVG: ${error.message}`, error); + }); +} + +function checkIfEllipse(svgelements, index) { + if (svgelements[index].tagName.toLowerCase() !== 'ellipse') { + return false + } + if (svgelements[index + 1] && svgelements[index + 1].tagName.toLowerCase() === 'path') { + return svgelements[index + 1].getAttribute('fill') !== '#000000' + } + if (svgelements[index + 1] && svgelements[index + 1].tagName.toLowerCase() === 'line') { + svgelements[index].setAttribute('fill', 'transparent') + svgelements[index].setAttribute('style', 'stroke:#222222;stroke-width:1.5;fill:transparent;') + } + return true +} + +function checkIfConnector(svgelements, index) { + if (svgelements[index].tagName.toLowerCase() !== 'ellipse') { + return false + } + if (svgelements[index + 1] && svgelements[index + 1].tagName.toLowerCase() === 'path') { + return svgelements[index + 1].getAttribute('fill') == '#000000' + } + return false + +} + +function checkIfCorrectPoly(svgelements, index) { + return ( + svgelements[index].tagName.toLowerCase() === 'polygon' && + svgelements[index + 1] && + ['text', 'a'].includes(svgelements[index + 1].tagName.toLowerCase()) && // Corrected this line + svgelements[index].getAttribute('style') === "stroke:#181818;stroke-width:0.5;" // Ensure the style matches exactly + ); +} + + +function checkIfMergePoly(svgelements, index) { + points = [] + let uniqueTuples = []; + if (svgelements[index].tagName.toLowerCase() === 'polygon') { + points = svgelements[index].getAttribute('points').split(",") + let tuples = []; + for (let i = 0; i < points.length - 1; i += 2) { + tuples.push([points[i], points[i + 1]]); + } + let seen = new Set(); + + for (let tuple of tuples) { + // Create a unique identifier for each tuple by joining its elements + let identifier = tuple.join('|'); + + if (!seen.has(identifier)) { + seen.add(identifier); + uniqueTuples.push(tuple); + } + } + } + + + return (svgelements[index].tagName.toLowerCase() === 'polygon' && uniqueTuples.length != 6 && + svgelements[index].getAttribute('style') == "stroke:#181818;stroke-width:0.5;") + // match on style and length of points to only match on the cube polygon at end of merge +} + +function checkIfActivity(svgelements, index) { + return (svgelements[index].tagName.toLowerCase() === 'rect') && parseFloat(svgelements[index].getAttribute('height')) > 6 && + (svgelements[index].getAttribute('style') == "stroke:#181818;stroke-width:0.5;") +} + +function checkIfTitleRect(svgelements, index) { + if (svgelements[index]) { + return (svgelements[index].tagName.toLowerCase() === 'rect') && parseFloat(svgelements[index].getAttribute('height')) > 6 && + (svgelements[index].getAttribute('style') == "stroke:#00000000;stroke-width:1.0;fill:none;") + } +} + +function checkIfFork(svgelements, index) { + return (svgelements[index].tagName.toLowerCase() === 'rect') && parseFloat(svgelements[index].getAttribute('height')) == 6 +} + +function checkIfNote(svgelements, index) { + return (svgelements[index].tagName.toLowerCase() === 'path' && svgelements[index + 1].tagName.toLowerCase() === 'path') +} + +function checkIfGroup(svgelements, index) { + if (index > 0 && (svgelements[index].tagName.toLowerCase() === 'text')) { + if (svgelements[index - 1].getAttribute('style') == "stroke:#000000;stroke-width:1.5;") + return true + return false + } + return false +} + +function checkIfWhile(svgelements, index) { + if (!svgelements[index + 3]) { // solves bug where last 2 svg elements are polygon and text from an arrow label + return false + } + + let points = []; + let yValues = []; + let xValues = []; + let text_y = ""; + let text_x = ""; + + // Check if there is a text element at index + 3 + if (svgelements[index + 3] && svgelements[index + 3].tagName.toLowerCase() !== 'text') { + return false; + } + + // Check if there is a polygon at index and text at index + 1 + if (svgelements[index + 1] && svgelements[index].tagName.toLowerCase() === 'polygon' && svgelements[index + 1].tagName.toLowerCase() === 'text') { + points = svgelements[index].getAttribute('points').split(","); + + // Extract y values from points + for (let i = 1; i < points.length; i += 2) { + yValues.push(parseFloat(points[i])); + } + + // Extract x values from points + for (let i = 0; i < points.length; i += 2) { + xValues.push(parseFloat(points[i])); + } + + // Get the text element y and x values and convert to float + text_y = parseFloat(svgelements[index + 1].getAttribute('y')); // the first text element following while polygon always has a higher y value + text_x = parseFloat(svgelements[index + 3].getAttribute('x')); // the 3rd text element following while polygon always has smaller x value + + // Check y values + for (const y of yValues) { + if (y > text_y) { + return false; + } + } + + // Check x values + for (const x of xValues) { + if (x < text_x) { + return false; + } + } + + return true; + } + + return false; +} + +function checkIfArrowLabel(svgelements, index) { + if (index > 0 && svgelements[index].tagName.toLowerCase() === 'text') { + let previousElement = svgelements[index - 1]; + if (previousElement.tagName.toLowerCase() === 'polygon' && + (previousElement.getAttribute('style')?.includes('stroke-width:1.0'))) { + return true; + } + } + return false; +} + +async function checkRepeat(puml, svgfile, svgelem) { + const pumlcontent = puml + const svg = svgfile + const svgelement = svgelem + try { + const response = await fetch("checkWhatPoly", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + const text = await response.text() + return (text == "repeat") + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + +} + +async function checkSwitch(puml, svgfile, svgelem) { + const pumlcontent = puml + const svg = svgfile + const svgelement = svgelem + try { + const response = await fetch("checkWhatPoly", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + const text = await response.text() + return (text.startsWith("switch")) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + +} + + + +async function checkIfRepeatHasBackward(puml, svg, svgelem) { + const pumlcontent = puml + const svgelement = svgelem + try { + const response = await fetch("checkIfRepeatHasBackward", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + const text = await response.text() + return (text == "backward") + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + + +} +async function checkBackward(puml, svgfile, svgelem) { + const pumlcontent = puml + const svg = svgfile + const svgelement = svgelem + try { + const response = await fetch("checkBackward", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + const text = await response.text() + return (text.startsWith("backward")) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + +} + + +async function handleContextMenuActivity(pumlcontent, svg, svgelement) { + const isBackward = await checkBackward(pumlcontent, svg, svgelement); + if (isBackward) { + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement; + e.preventDefault(); + var contextMenu = document.getElementById('backward-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + } else { + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement; + e.preventDefault(); + var contextMenu = document.getElementById('activity-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + } +} + + +async function handleContextMenuPoly(pumlcontent, svg, svgelement) { + const isSwitch = await checkSwitch(pumlcontent, svg, svgelement) + const isRepeat = await checkRepeat(pumlcontent, svg, svgelement) + const hasBackward = await checkIfRepeatHasBackward(pumlcontent, svg, svgelement) + if (isRepeat) { + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement; + e.preventDefault(); + var contextMenu = document.getElementById('repeat-menu'); + var backwardButton = document.getElementById('addbackwards'); + if (hasBackward) { + backwardButton.classList.add('disabled'); + } else { + backwardButton.classList.remove('disabled'); + } + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + } else if (isSwitch) { + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement; + e.preventDefault(); + var contextMenu = document.getElementById('switch-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + + + + } else { + svgelement.addEventListener('contextmenu', function(e) { + lastclickedsvgelement = svgelement; + e.preventDefault(); + var contextMenu = document.getElementById('if-menu'); + contextMenu.style.display = 'block'; + contextMenu.style.left = e.pageX + 'px'; + contextMenu.style.top = e.pageY + 'px'; + }); + } +} + + +function process(text) { + const lines = text.split('\n'); + let i = 0; + + while (i < lines.length) { + if (lines[i].trim() === 'fork again') { + // Check if there's a "fork" above it OR an "end fork" or "end merge" below it OR another "fork again" above it + if ((i > 0 && lines[i - 1].trim() === 'fork') || + (i < lines.length - 1 && (lines[i + 1].trim() === 'end fork' || lines[i + 1].trim() === 'end merge')) || + (i > 0 && lines[i - 1].trim() === 'fork again')) { + lines.splice(i, 1); // Delete the current "fork again" line + } + } + i++; + } + + i = 0; + while (i < lines.length) { + if (lines[i].trim().startsWith("case")) { + if ((i < lines.length - 1 && (lines[i + 1].trim().startsWith("case") || lines[i + 1].trim() === 'endswitch')) || + (i > 0 && lines[i - 1].trim().startsWith("case"))) { + lines.splice(i, 1); // Delete the current "case" line + } + } + i++; + } + return lines.join('\n'); +} + + + +function labelForks(puml) { + const queue = []; + const lines = puml.split('\n'); + + for (let index = 0; index < lines.length; index++) { + const line = lines[index]; + const trimmedLine = line.trim(); + if (trimmedLine.startsWith('fork') && !trimmedLine.startsWith('fork again')){ + queue.push({ + index, + line: 'top' + }); + } else if (trimmedLine.startsWith("end fork") || trimmedLine.startsWith("endfork")) { + queue.push({ + index, + line: 'bottom' + }); + } + } + + return queue; + +} + +function setPuml(pumlcontent) { + text = process(pumlcontent) + text = indentPuml(text) + editor.session.setValue(text) +} + +function indentPuml(pumlcontent) { + const lines = pumlcontent.split('\n'); + let indentedLines = []; + let level = 0; + + // Keywords that increase indentation level + const increaseIndentKeywords = ["If", "if", "while", "group", "partition", "fork", "repeat", "switch"] + // Keywords that should go back one level and then keep same level + const sameIndentKeywords = ["else", "fork again", "case"] + // Keywords that decrease indentation level + const decreaseIndentKeywords = ["endif", "endwhile", "end group", "}", "end fork", "endfork", "end merge", "repeat while", "repeatwhile", "endswitch"] + + lines.forEach(line => { + const trimmedLine = line.trim(); + + if (trimmedLine.startsWith("repeat while") || trimmedLine.startsWith("repeatwhile")) { + level--; + indentedLines.push(' '.repeat(level) + trimmedLine); + return + } + + if ((trimmedLine.startsWith('fork') && !trimmedLine.startsWith('fork again')) || trimmedLine == "repeat") { + indentedLines.push(' '.repeat(level) + trimmedLine); + level++; + return + } + + + // Adjust indentation before appending the line if it is an end statement + if (decreaseIndentKeywords.some(keyword => trimmedLine.startsWith(keyword))) { + level--; + } + + if (sameIndentKeywords.some(keyword => trimmedLine.startsWith(keyword))) { + indentedLines.push(' '.repeat(level - 1) + trimmedLine); + return + + } else { + indentedLines.push(' '.repeat(level) + trimmedLine); + } + + // Adjust indentation after appending the line if it is a start statement + if (increaseIndentKeywords.some(keyword => trimmedLine.startsWith(keyword))) { + level++; + } + }); + + return indentedLines.join('\n'); +} + +function getHashParameter() { + const query = window.location.search.substring(1); // Remove the leading "?" + return query ? query : null; +} + +async function processActivityLine(pumlcontent, svg, svgelement) { + try { + const response = await fetch("getActivityLine", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + const data = await response.json(); + number = data.result + getmarker(number) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + +} + +async function processNoteLine(pumlcontent, svg, svgelement) { + try { + const response = await fetch("getNoteLine", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + const data = await response.json(); + number = data.result + getmarker(number) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + +} + +async function processEllipseLine(pumlcontent, svg, svgelement) { + try { + const response = await fetch("getEllipseLine", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + + const data = await response.json(); + number = data.result - 1 + getmarkersinglelines(number) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + +} + +async function processConnectorLine(pumlcontent, svg, svgelement) { + try { + const response = await fetch("getConnectorLine", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + + const data = await response.json(); + number = data.result - 1 + getmarkersinglelines(number) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + +} + +async function processGroupLine(pumlcontent, svg, svgelement) { + try { + const response = await fetch("getGroupLine", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + + const data = await response.json(); + number = data.result + getmarkersinglelines(number) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + +} + +async function processIfLine(pumlcontent, svg, svgelement) { + try { + const response = await fetch("getIfLine", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + + const data = await response.json(); + number = data.result + getmarkersinglelines(number) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + +} + +async function processMergeLine(pumlcontent, svg, svgelement) { + try { + const response = await fetch("getMergeLine", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + + const data = await response.json(); + number = data.result + getmarkersinglelines(number) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + +} + +async function processWhileLine(pumlcontent, svg, svgelement) { + try { + const response = await fetch("getWhileLine", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + + const data = await response.json(); + number = data.result + getmarkersinglelines(number) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + + +} + +async function processArrowLine(pumlcontent, svg, svgelement) { + try { + const response = await fetch("getArrowLine", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent, + 'svg': svg.innerHTML, + 'svgelement': svgelement.outerHTML + }) + }); + + const data = await response.json(); + number = data.result + getmarkersinglelines(number) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + +} + +async function processTitleLine(pumlcontent) { + try { + const response = await fetch("getTitleLine", { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + 'plantuml': pumlcontent + }) + }); + + const data = await response.json(); + number = data.result + getmarker(number) + + } catch (error) { + displayErrorMessage(`Error with fetch API: ${error.message}`, error); + } + +} + +function highlightActivity(svg, text) { + const rects = svg.getElementsByTagName("rect"); + + for (let i = 0; i < rects.length; i++) { + const rect = rects[i]; + + // Check if this is an "activity" rect using the check function + if (checkIfActivity(rects, i)) { + let combinedText = ""; + let nextSibling = rect.nextElementSibling; + + // Collect all subsequent nodes that are either or elements + while (nextSibling && (nextSibling.tagName === "text" || nextSibling.tagName === "a")) { + if (nextSibling.tagName === "text") { + // If it's a text node, simply collect its text content + combinedText += nextSibling.textContent + "\n"; + } else if (nextSibling.tagName === "a") { + // If it's an tag, collect the link href and text content + const href = nextSibling.getAttribute("href"); + const linkTextElement = nextSibling.querySelector("text"); + if (linkTextElement) { + const linkText = linkTextElement.textContent; + combinedText += `[[${href} ${linkText}]]`; + } + } + + // Move to the next sibling element + nextSibling = nextSibling.nextElementSibling; + } + + // Compare the normalized text + if (combinedText.replace(/\s+/g, "") === text.replace(/\s+/g, "")) { + colorqueue.push(rect.getAttribute('fill')); + rect.setAttribute('fill', "#d8d8d8"); + } + } + } +} + + + + + +function resetHighlight(svg) { + if (svg) { + const rects = svg.getElementsByTagName("rect"); + + for (let i = 0; i < rects.length; i++) { + const rect = rects[i]; + if (checkIfActivity(rects, i)) { + if (rect.getAttribute('fill') == "#d8d8d8") { + rect.setAttribute('fill', colorqueue.shift()) + } + } + } + colorqueue = [] + } +} + +function trimlines(pumlcontent) { + + return pumlcontent.split('\n').map(line => line.trim()).join('\n'); + +} + +function getmarker(bounds) { + clearMarkers() + editor.session.addMarker(new Range(bounds[0], 0, bounds[1], 200), "hover", "fullLine"); + +} + +function getmarkersinglelines(bounds) { + clearMarkers() + if (typeof bounds === 'number') { + editor.session.addMarker(new Range(bounds, 0, bounds, 200), "hover", "fullLine"); + } else { + for (let bound of bounds) { + editor.session.addMarker(new Range(bound, 0, bound, 200), "hover", "fullLine"); + } + } +} + +function clearMarkers() { + const prevMarkers = editor.session.getMarkers(); + + if (prevMarkers) { + const prevMarkersArr = Object.keys(prevMarkers); + + for (let item of prevMarkersArr) { + const marker = prevMarkers[item]; + + // Remove marker if it isnt the active line + if (marker.clazz == "hover") { + editor.session.removeMarker(marker.id); + } + } + } +} + +function undoeditor() { + if (historyPointer > 0) { + historyPointer--; + setPuml(history[historyPointer]) + } +} + +function restoreeditor() { + if (historyPointer < history.length - 1) { + historyPointer++; + setPuml(history[historyPointer]) + } +} diff --git a/src/plantuml_gui/static/styles.css b/src/plantuml_gui/static/styles.css new file mode 100644 index 0000000..c8f8b5c --- /dev/null +++ b/src/plantuml_gui/static/styles.css @@ -0,0 +1,146 @@ + +html, body { + height: 100%; + width: 100%; + margin: 0; + display: flex; +} + +#left-side { + display: flex; + flex-direction: column; + width: 40%; + height: 100%; +} + +#colb { + display: flex; + flex-direction: column; + width: 60%; + height: 100%; +} + +#header { + background-color: #dcdcdc; + padding: 10px; + box-sizing: border-box; + border: 2px solid black; + border-right: none; + display: flex; + flex-direction: column; + position: relative; /* Ensure absolute positioning within header works */ +} + +#editor { + visibility: hidden; /* at the beginning we make it hidden so that decode can happen before setting the demo */ + flex: 1; + font-size: 16px; +} + +#colb-container { + display: flex; + width: 60%; + padding: 10px; + box-sizing: border-box; + justify-content: center; + align-items: flex-start; + border: 2px solid black; + overflow-x: auto; + overflow-y: hidden; + position: relative; /* This ensures the overlay is confined to this container */ +} + +#loading-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(255, 255, 255, 0.7); /* Semi-transparent white */ + z-index: 10; /* Make sure it's on top */ + display: none; /* Hidden by default */ +} + +#colb { + width: 100%; + height: auto; + display: flex; + justify-content: center; + align-items: center; + overflow: visible; + position: relative; +} + +#colb svg { + width: 100%; + height: auto; + display: block; + max-width: none; + overflow: visible; +} + +#init-text { + text-align: center; +} + +#version { + font-size: 14px; +} + +#issue { + position: relative; + align-self: flex-end; + margin-top: auto; + font-size: 18px; + bottom: 5px; + right: 5px; +} + +.button-groups { + display: flex; + flex-direction: row; + gap: 10px; /* Adds space between the button groups */ +} + +.btn-group { + width: auto; /* Allows button groups to size based on content */ +} + + +.dropdown-menu li { + position: relative; +} + +.dropdown-menu .dropdown-submenu { + display: none; + position: absolute; + left: 100%; + top: -7px; +} + +.dropdown-menu .dropdown-submenu-left { + right: 100%; + left: auto; +} + +.dropdown-menu > li:hover > .dropdown-submenu { + display: block; +} + +#popup { + background-color: #eb604b; + padding: 10px; + position: fixed; + top: 0; + right: 0; + color: black; + border: 2px solid black; + visibility: hidden; /* Popup will be hidden by default */ + z-index: 9999; /* Ensure it appears above other content */ +} + + +.hover { + position: absolute; + background-color: #56596a; + } diff --git a/src/plantuml_gui/static/theme-dracula.js b/src/plantuml_gui/static/theme-dracula.js new file mode 100644 index 0000000..d98e684 --- /dev/null +++ b/src/plantuml_gui/static/theme-dracula.js @@ -0,0 +1,21 @@ +define("ace/theme/dracula-css", ["require", "exports", "module"], function(require, exports, module) { + module.exports = "/*\n * Copyright \u00A9 2017 Zeno Rocha \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \u201CSoftware\u201D), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \u201CAS IS\u201D, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n.ace-dracula .ace_gutter {\n background: #282a36;\n color: rgb(144,145,148)\n}\n\n.ace-dracula .ace_print-margin {\n width: 1px;\n background: #44475a\n}\n\n.ace-dracula {\n background-color: #282a36;\n color: #f8f8f2\n}\n\n.ace-dracula .ace_cursor {\n color: #f8f8f0\n}\n\n.ace-dracula .ace_marker-layer .ace_selection {\n background: #44475a\n}\n\n.ace-dracula.ace_multiselect .ace_selection.ace_start {\n box-shadow: 0 0 3px 0px #282a36;\n border-radius: 2px\n}\n\n.ace-dracula .ace_marker-layer .ace_step {\n background: rgb(198, 219, 174)\n}\n\n.ace-dracula .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #a29709\n}\n\n.ace-dracula .ace_marker-layer .ace_active-line {\n background: #44475a\n}\n\n.ace-dracula .ace_gutter-active-line {\n background-color: #44475a\n}\n\n.ace-dracula .ace_marker-layer .ace_selected-word {\n box-shadow: 0px 0px 0px 1px #a29709;\n border-radius: 3px;\n}\n\n.ace-dracula .ace_fold {\n background-color: #50fa7b;\n border-color: #f8f8f2\n}\n\n.ace-dracula .ace_keyword {\n color: #ff79c6\n}\n\n.ace-dracula .ace_constant.ace_language {\n color: #bd93f9\n}\n\n.ace-dracula .ace_constant.ace_numeric {\n color: #bd93f9\n}\n\n.ace-dracula .ace_constant.ace_character {\n color: #bd93f9\n}\n\n.ace-dracula .ace_constant.ace_character.ace_escape {\n color: #ff79c6\n}\n\n.ace-dracula .ace_constant.ace_other {\n color: #bd93f9\n}\n\n.ace-dracula .ace_support.ace_function {\n color: #8be9fd\n}\n\n.ace-dracula .ace_support.ace_constant {\n color: #6be5fd\n}\n\n.ace-dracula .ace_support.ace_class {\n font-style: italic;\n color: #66d9ef\n}\n\n.ace-dracula .ace_support.ace_type {\n font-style: italic;\n color: #66d9ef\n}\n\n.ace-dracula .ace_storage {\n color: #ff79c6\n}\n\n.ace-dracula .ace_storage.ace_type {\n font-style: italic;\n color: #8be9fd\n}\n\n.ace-dracula .ace_invalid {\n color: #F8F8F0;\n background-color: #ff79c6\n}\n\n.ace-dracula .ace_invalid.ace_deprecated {\n color: #F8F8F0;\n background-color: #bd93f9\n}\n\n.ace-dracula .ace_string {\n color: #f1fa8c\n}\n\n.ace-dracula .ace_comment {\n color: #6272a4\n}\n\n.ace-dracula .ace_variable {\n color: #50fa7b\n}\n\n.ace-dracula .ace_variable.ace_parameter {\n font-style: italic;\n color: #ffb86c\n}\n\n.ace-dracula .ace_entity.ace_other.ace_attribute-name {\n color: #50fa7b\n}\n\n.ace-dracula .ace_entity.ace_name.ace_function {\n color: #50fa7b\n}\n\n.ace-dracula .ace_entity.ace_name.ace_tag {\n color: #ff79c6\n}\n.ace-dracula .ace_invisible {\n color: #626680;\n}\n\n.ace-dracula .ace_indent-guide {\n background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYHB3d/8PAAOIAdULw8qMAAAAAElFTkSuQmCC) right repeat-y\n}\n\n.ace-dracula .ace_indent-guide-active {\n background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACAQMAAACjTyRkAAAABlBMVEUAAADCwsK76u2xAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjYGBoAAAAhACBGFbxzQAAAABJRU5ErkJggg==\") right repeat-y;\n}\n"; + +}); + +define("ace/theme/dracula", ["require", "exports", "module", "ace/theme/dracula-css", "ace/lib/dom"], function(require, exports, module) { + exports.isDark = true; + exports.cssClass = "ace-dracula"; + exports.cssText = require("./dracula-css"); + exports.$selectionColorConflict = true; + var dom = require("../lib/dom"); + dom.importCssString(exports.cssText, exports.cssClass, false); + +}); +(function() { + window.require(["ace/theme/dracula"], function(m) { + if (typeof module == "object" && typeof exports == "object" && module) { + module.exports = m; + } + }); +})(); diff --git a/src/plantuml_gui/static/theme-one_dark.js b/src/plantuml_gui/static/theme-one_dark.js new file mode 100644 index 0000000..319d3fe --- /dev/null +++ b/src/plantuml_gui/static/theme-one_dark.js @@ -0,0 +1,20 @@ +define("ace/theme/one_dark-css", ["require", "exports", "module"], function(require, exports, module) { + module.exports = ".ace-one-dark .ace_gutter {\n background: #282c34;\n color: #6a6f7a\n}\n\n.ace-one-dark .ace_print-margin {\n width: 1px;\n background: #e8e8e8\n}\n\n.ace-one-dark {\n background-color: #282c34;\n color: #abb2bf\n}\n\n.ace-one-dark .ace_cursor {\n color: #528bff\n}\n\n.ace-one-dark .ace_marker-layer .ace_selection {\n background: #3d4350\n}\n\n.ace-one-dark.ace_multiselect .ace_selection.ace_start {\n box-shadow: 0 0 3px 0 #282c34;\n border-radius: 2px\n}\n\n.ace-one-dark .ace_marker-layer .ace_step {\n background: #c6dbae\n}\n\n.ace-one-dark .ace_marker-layer .ace_bracket {\n margin: -1px 0 0 -1px;\n border: 1px solid #747369\n}\n\n.ace-one-dark .ace_marker-layer .ace_active-line {\n background: rgba(76, 87, 103, .19)\n}\n\n.ace-one-dark .ace_gutter-active-line {\n background-color: rgba(76, 87, 103, .19)\n}\n\n.ace-one-dark .ace_marker-layer .ace_selected-word {\n border: 1px solid #3d4350\n}\n\n.ace-one-dark .ace_fold {\n background-color: #61afef;\n border-color: #abb2bf\n}\n\n.ace-one-dark .ace_keyword {\n color: #c678dd\n}\n\n.ace-one-dark .ace_keyword.ace_operator {\n color: #c678dd\n}\n\n.ace-one-dark .ace_keyword.ace_other.ace_unit {\n color: #d19a66\n}\n\n.ace-one-dark .ace_constant.ace_language {\n color: #d19a66\n}\n\n.ace-one-dark .ace_constant.ace_numeric {\n color: #d19a66\n}\n\n.ace-one-dark .ace_constant.ace_character {\n color: #56b6c2\n}\n\n.ace-one-dark .ace_constant.ace_other {\n color: #56b6c2\n}\n\n.ace-one-dark .ace_support.ace_function {\n color: #61afef\n}\n\n.ace-one-dark .ace_support.ace_constant {\n color: #d19a66\n}\n\n.ace-one-dark .ace_support.ace_class {\n color: #e5c07b\n}\n\n.ace-one-dark .ace_support.ace_type {\n color: #e5c07b\n}\n\n.ace-one-dark .ace_storage {\n color: #c678dd\n}\n\n.ace-one-dark .ace_storage.ace_type {\n color: #c678dd\n}\n\n.ace-one-dark .ace_invalid {\n color: #fff;\n background-color: #f2777a\n}\n\n.ace-one-dark .ace_invalid.ace_deprecated {\n color: #272b33;\n background-color: #d27b53\n}\n\n.ace-one-dark .ace_string {\n color: #98c379\n}\n\n.ace-one-dark .ace_string.ace_regexp {\n color: #e06c75\n}\n\n.ace-one-dark .ace_comment {\n font-style: italic;\n color: #5c6370\n}\n\n.ace-one-dark .ace_variable {\n color: #e06c75\n}\n\n.ace-one-dark .ace_variable.ace_parameter {\n color: #d19a66\n}\n\n.ace-one-dark .ace_meta.ace_tag {\n color: #e06c75\n}\n\n.ace-one-dark .ace_entity.ace_other.ace_attribute-name {\n color: #e06c75\n}\n\n.ace-one-dark .ace_entity.ace_name.ace_function {\n color: #61afef\n}\n\n.ace-one-dark .ace_entity.ace_name.ace_tag {\n color: #e06c75\n}\n\n.ace-one-dark .ace_markup.ace_heading {\n color: #98c379\n}\n\n.ace-one-dark .ace_indent-guide {\n background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ09NrYAgMjP4PAAtGAwchHMyAAAAAAElFTkSuQmCC) right repeat-y\n}\n\n.ace-one-dark .ace_indent-guide-active {\n background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQIW2PQ1dX9zzBz5sz/ABCcBFFentLlAAAAAElFTkSuQmCC) right repeat-y;\n}\n"; + +}); + +define("ace/theme/one_dark", ["require", "exports", "module", "ace/theme/one_dark-css", "ace/lib/dom"], function(require, exports, module) { + exports.isDark = true; + exports.cssClass = "ace-one-dark"; + exports.cssText = require("./one_dark-css"); + var dom = require("../lib/dom"); + dom.importCssString(exports.cssText, exports.cssClass, false); + +}); +(function() { + window.require(["ace/theme/one_dark"], function(m) { + if (typeof module == "object" && typeof exports == "object" && module) { + module.exports = m; + } + }); +})(); diff --git a/src/plantuml_gui/templates/index.html b/src/plantuml_gui/templates/index.html new file mode 100644 index 0000000..8d07d44 --- /dev/null +++ b/src/plantuml_gui/templates/index.html @@ -0,0 +1,781 @@ + + + + + PlantUML Interactive Editor + + + + + + + + + + + + + + + + + + +

+
+ +
+
+
+
+
+

PlantUML is Loading

+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plantuml_gui/title.py b/src/plantuml_gui/title.py new file mode 100644 index 0000000..15e4dc6 --- /dev/null +++ b/src/plantuml_gui/title.py @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +def add_title(puml): + index = 0 + lines = puml.splitlines() + for index, line in enumerate(lines): + if line == "@startuml": + if lines[index + 1].startswith("title"): + break + else: + lines.insert(index + 1, "endtitle") + lines.insert(index + 1, "Placeholder Title") + lines.insert(index + 1, "title") + break + return "\n".join(lines) + + +def get_title_text(puml): + lines = puml.splitlines() + intitle = False + text = "" + for line in lines: + if line == "endtitle" or line == "end title": + break + if intitle: + if text == "": + text += line + else: + text += "\n" + line + if line == "title": + intitle = True + return text + + +def edit_title_text(puml, title): + lines = puml.splitlines() + title_lines = title.splitlines() + start, end = find_title_bounds(lines) + + if title == "": + del lines[start : end + 1] + return "\n".join(lines) + + lines[start + 1 : end] = title_lines + return "\n".join(lines) + + +def find_title_bounds(lines): + start, end = -1, -1 + for index, line in enumerate(lines): + if line.startswith("title"): + start = index + if line.startswith("endtitle") or line.startswith("end title"): + end = index + return start, end + + +def delete_title(puml): + lines = puml.splitlines() + start, end = find_title_bounds(lines) + del lines[start : end + 1] + return "\n".join(lines) diff --git a/src/plantuml_gui/util.py b/src/plantuml_gui/util.py new file mode 100644 index 0000000..24cfb68 --- /dev/null +++ b/src/plantuml_gui/util.py @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from plantuml_gui.classes import PolyElement, SvgChunk + + +def index_of_clicked_element(svgchunklist: list[SvgChunk], clickedelement) -> int: + count = 0 + for svgchunk in svgchunklist: + count += 1 + if svgchunk.object == clickedelement: + break + return count + + +def checkifwhile(svgchunk: SvgChunk): + if len(svgchunk.text_elements) < 3 or type(svgchunk.object) != PolyElement: + return False + points = svgchunk.object.points.split(",") + y_values = [] + x_values = [] + text_y_value = svgchunk.text_elements[0].y + text_x_value = svgchunk.text_elements[2].x + + for i in range(1, len(points), 2): + y_values.append(float(points[i])) + for y in y_values: + if y > text_y_value: + return False + + for i in range(0, len(points), 2): + x_values.append(float(points[i])) + for x in x_values: + if x < text_x_value: + return False + + return True diff --git a/src/plantuml_gui/whilepoly.py b/src/plantuml_gui/whilepoly.py new file mode 100644 index 0000000..da748b2 --- /dev/null +++ b/src/plantuml_gui/whilepoly.py @@ -0,0 +1,185 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import re + +from plantuml_gui.util import checkifwhile + +from .classes import PolyElement, SvgChunk + + +def whiletotext(svgchunklist: list[SvgChunk], clickedelement: PolyElement) -> list[str]: + texts = [] + for chunk in svgchunklist: + if chunk.object == clickedelement: + texts.append(chunk.text_elements[1].label) + texts.append(chunk.text_elements[2].label) + texts.append(chunk.text_elements[0].label) + break + return texts + + +def find_index_loop(puml, svgchunklist, clickedelement): + count = whilecount(svgchunklist, clickedelement) + lines = puml.splitlines() + while_start = findwhilebounds(lines, count) + endwhile_start = findendwhilebounds(lines, while_start) + return endwhile_start + + +def find_index_break(puml, svgchunklist, clickedelement): + count = whilecount(svgchunklist, clickedelement) + lines = puml.splitlines() + while_start = findwhilebounds(lines, count) + endwhile_start = findendwhilebounds(lines, while_start) + return endwhile_start + 1 + + +def delete_while(puml, svgchunklist, clickedelement): + count = whilecount(svgchunklist, clickedelement) + lines = puml.splitlines() + while_start = findwhilebounds(lines, count) + endwhile_start = findendwhilebounds(lines, while_start) + del lines[while_start : endwhile_start + 1] + return "\n".join(lines) + + +def editwhile( + puml: str, + svgchunklist: list[SvgChunk], + whilestatement: str, + breakstatement: str, + loop: str, + clickedelement, +): + count = whilecount(svgchunklist, clickedelement) + lines = puml.splitlines() + while_start = findwhilebounds(lines, count) + + while_text = lines[while_start] + while_text = re.sub( + r"while \((.*?)\)", + f"while ({whilestatement})", + while_text, + flags=re.DOTALL, + ) + while_text = re.sub( + r"is \((.*?)\)", + f"is ({loop})", + while_text, + flags=re.DOTALL, + ) + + lines[while_start] = while_text + + endwhile_start = findendwhilebounds(lines, while_start) + endwhile_text = lines[endwhile_start] + endwhile_text = re.sub( + r"endwhile \((.*?)\)", + f"endwhile ({breakstatement})", + endwhile_text, + flags=re.DOTALL, + ) + lines[endwhile_start] = endwhile_text + + return "\n".join(lines) + + +def findendwhilebounds(lines, start_while): + start_endwhile = -1 + index = start_while + + level = 0 + while index < len(lines): + line = lines[index] + clean_line = line.strip() + + if level == 1: + if clean_line.startswith("endwhile"): + start_endwhile = index + break + + if clean_line.startswith("while"): + level += 1 + + if level != 1 and clean_line.startswith("endwhile"): + level -= 1 # pragma: no cover + + index += 1 + + return start_endwhile + + +def findwhilebounds(lines, count): + start_while = -1 + index = 0 + nesting_level = 0 + nested_stacks = [] + current_stack = [] + + # Traverse the lines to find all while statements and their nesting levels + while index < len(lines): + line = lines[index] + clean_line = line.strip() + if clean_line.startswith("while"): + current_stack.append((index, nesting_level)) + nesting_level += 1 + elif clean_line.startswith("endwhile"): + nesting_level -= 1 + if nesting_level == 0: + # Sort the current stack and add to nested_stacks + current_stack.sort(key=lambda x: x[1], reverse=True) + nested_stacks.append(current_stack) + current_stack = [] + index += 1 + + # Combine all nested stacks + combined_stack = [item for sublist in nested_stacks for item in sublist] + + # Traverse the combined stack to find the correct while based on count + for index, level in combined_stack: + if count == 1: + start_while = index + break + count -= 1 + + return start_while + + +def get_while_line(puml, svgchunklist, clickedelement): + count = whilecount(svgchunklist, clickedelement) + lines = puml.splitlines() + while_start = findwhilebounds(lines, count) + while_end = findendwhilebounds(lines, while_start) + return while_start, while_end + + +def whilecount(svgchunklist: list[SvgChunk], clickedelement: PolyElement): + count = 0 + for svgchunk in svgchunklist: # counts occurances of while polys + if svgchunk.text_elements and checkifwhile(svgchunk): + count += 1 + if svgchunk.object == clickedelement: + break + return count diff --git a/tests/__pycache__/conftest.cpython-311-pytest-8.2.2.pyc b/tests/__pycache__/conftest.cpython-311-pytest-8.2.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8afba7e4c257443599d11240ec9e681dd9cb74a8 GIT binary patch literal 785 zcmZutzi-n(6nFiVVyWtgfC!2}lnewUd*7*!%^U$$k z?SBA5{1^NMsgxnQ2?;T<1!e2RvlB=ez_vI=EL&mdZr=s4HyIG5RmixPyJ*gV z6Ljk_+Th|V_yQSpC04?FLgO^zrtP^j;9p^xhF3fSEQKpA%Jfb$&L(d9$ z?0ff4VpmVYBHbPb$0_QC6QQhcCsAi`W4`ta1*B$j d_4L*SY0XG$AraD9;9H$PGDuenN+xIj{R^dE!QlV^ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_app.cpython-311-pytest-8.2.2.pyc b/tests/__pycache__/test_app.cpython-311-pytest-8.2.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb11593fca466e8ea1d6ed015f79e451208ed654 GIT binary patch literal 621431 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 Lw;I$kiT(cpxaVI* literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_if_statements.cpython-311-pytest-8.2.2.pyc b/tests/__pycache__/test_if_statements.cpython-311-pytest-8.2.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..458db96a22d76edcdc03633a387313fce24d17cd GIT binary patch literal 1084 zcmZ`&J!}&(6t?dtxui`C5=%iK)J~BgQISv?P(_j20aX=J)k@7`U2{&j%6AvpPLsgW zfsqweVqyxQE-Vael!>vGI#iZAF|h@eq3Xo5uSpZBy*q#R-uvw5=l9)xDirbvuENjp z`ga$h-`eS4)`fAp1ja53P=Fb#VDq*jyJ}$+ITaVDOs0|n#$wsXt9T}6vs{#~EUe2;({lW0{b!R=p_1ygWb~ z#_4B(T_mxI8rT}x(z%RwA-XqU9wUro_L2>7V9DH&Z;#FooG#?r8{{)5gA;e5JA3^O zLCyfJq8T)cgwwLx_6BNUj5Z*iGDUYqc@5^rvKcXDi|x9~G!i(TP!6LtS#-hgNGhbt za6c4OsQkLmnskx#gmb++*N6UsPIam0^W|4bM9XyB=bMz58$8*fRax%u@UY$t%Q~Ad z2rVQ+`qK1Tlo}niq)DP%4pbNiv`x}&32j$tLxxG5GTS~215!<*%`o;&IH?hz*PB{4 z!EZn@_h5)Fzz#aeO}@MP{`FyQ`Y1QuL)cx%DV}&UA76ZRzrFeKrq>9g zrn)^sji{7ruKUXi8YVPM0-SqKqa + + + + Jasmine Spec Runner v5.1.2 + + + + + + + + + + + + + + + + + + + + diff --git a/tests/js/lib/jasmine-5.1.2/boot0.js b/tests/js/lib/jasmine-5.1.2/boot0.js new file mode 100644 index 0000000..d9afe76 --- /dev/null +++ b/tests/js/lib/jasmine-5.1.2/boot0.js @@ -0,0 +1,65 @@ +/* +Copyright (c) 2008-2019 Pivotal Labs +Copyright (c) 2008-2024 The Jasmine developers + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/** + This file starts the process of "booting" Jasmine. It initializes Jasmine, + makes its globals available, and creates the env. This file should be loaded + after `jasmine.js` and `jasmine_html.js`, but before `boot1.js` or any project + source files or spec files are loaded. + */ +(function() { + const jasmineRequire = window.jasmineRequire || require('./jasmine.js'); + + /** + * ## Require & Instantiate + * + * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. + */ + const jasmine = jasmineRequire.core(jasmineRequire), + global = jasmine.getGlobal(); + global.jasmine = jasmine; + + /** + * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. + */ + jasmineRequire.html(jasmine); + + /** + * Create the Jasmine environment. This is used to run all specs in a project. + */ + const env = jasmine.getEnv(); + + /** + * ## The Global Interface + * + * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. + */ + const jasmineInterface = jasmineRequire.interface(jasmine, env); + + /** + * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. + */ + for (const property in jasmineInterface) { + global[property] = jasmineInterface[property]; + } +})(); diff --git a/tests/js/lib/jasmine-5.1.2/boot1.js b/tests/js/lib/jasmine-5.1.2/boot1.js new file mode 100644 index 0000000..33d105b --- /dev/null +++ b/tests/js/lib/jasmine-5.1.2/boot1.js @@ -0,0 +1,133 @@ +/* +Copyright (c) 2008-2019 Pivotal Labs +Copyright (c) 2008-2024 The Jasmine developers + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/** + This file finishes 'booting' Jasmine, performing all of the necessary + initialization before executing the loaded environment and all of a project's + specs. This file should be loaded after `boot0.js` but before any project + source files or spec files are loaded. Thus this file can also be used to + customize Jasmine for a project. + + If a project is using Jasmine via the standalone distribution, this file can + be customized directly. If you only wish to configure the Jasmine env, you + can load another file that calls `jasmine.getEnv().configure({...})` + after `boot0.js` is loaded and before this file is loaded. + */ + +(function() { + const env = jasmine.getEnv(); + + /** + * ## Runner Parameters + * + * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. + */ + + const queryString = new jasmine.QueryString({ + getWindowLocation: function() { + return window.location; + } + }); + + const filterSpecs = !!queryString.getParam('spec'); + + const config = { + stopOnSpecFailure: queryString.getParam('stopOnSpecFailure'), + stopSpecOnExpectationFailure: queryString.getParam( + 'stopSpecOnExpectationFailure' + ), + hideDisabled: queryString.getParam('hideDisabled') + }; + + const random = queryString.getParam('random'); + + if (random !== undefined && random !== '') { + config.random = random; + } + + const seed = queryString.getParam('seed'); + if (seed) { + config.seed = seed; + } + + /** + * ## Reporters + * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). + */ + const htmlReporter = new jasmine.HtmlReporter({ + env: env, + navigateWithNewParam: function(key, value) { + return queryString.navigateWithNewParam(key, value); + }, + addToExistingQueryString: function(key, value) { + return queryString.fullStringWithNewParam(key, value); + }, + getContainer: function() { + return document.body; + }, + createElement: function() { + return document.createElement.apply(document, arguments); + }, + createTextNode: function() { + return document.createTextNode.apply(document, arguments); + }, + timer: new jasmine.Timer(), + filterSpecs: filterSpecs + }); + + /** + * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. + */ + env.addReporter(jsApiReporter); + env.addReporter(htmlReporter); + + /** + * Filter which specs will be run by matching the start of the full name against the `spec` query param. + */ + const specFilter = new jasmine.HtmlSpecFilter({ + filterString: function() { + return queryString.getParam('spec'); + } + }); + + config.specFilter = function(spec) { + return specFilter.matches(spec.getFullName()); + }; + + env.configure(config); + + /** + * ## Execution + * + * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. + */ + const currentWindowOnload = window.onload; + + window.onload = function() { + if (currentWindowOnload) { + currentWindowOnload(); + } + htmlReporter.initialize(); + env.execute(); + }; +})(); diff --git a/tests/js/lib/jasmine-5.1.2/jasmine-html.js b/tests/js/lib/jasmine-5.1.2/jasmine-html.js new file mode 100644 index 0000000..0be90bf --- /dev/null +++ b/tests/js/lib/jasmine-5.1.2/jasmine-html.js @@ -0,0 +1,964 @@ +/* +Copyright (c) 2008-2019 Pivotal Labs +Copyright (c) 2008-2024 The Jasmine developers + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +// eslint-disable-next-line no-var +var jasmineRequire = window.jasmineRequire || require('./jasmine.js'); + +jasmineRequire.html = function(j$) { + j$.ResultsNode = jasmineRequire.ResultsNode(); + j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); + j$.QueryString = jasmineRequire.QueryString(); + j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); +}; + +jasmineRequire.HtmlReporter = function(j$) { + function ResultsStateBuilder() { + this.topResults = new j$.ResultsNode({}, '', null); + this.currentParent = this.topResults; + this.specsExecuted = 0; + this.failureCount = 0; + this.pendingSpecCount = 0; + } + + ResultsStateBuilder.prototype.suiteStarted = function(result) { + this.currentParent.addChild(result, 'suite'); + this.currentParent = this.currentParent.last(); + }; + + ResultsStateBuilder.prototype.suiteDone = function(result) { + this.currentParent.updateResult(result); + if (this.currentParent !== this.topResults) { + this.currentParent = this.currentParent.parent; + } + + if (result.status === 'failed') { + this.failureCount++; + } + }; + + ResultsStateBuilder.prototype.specStarted = function(result) {}; + + ResultsStateBuilder.prototype.specDone = function(result) { + this.currentParent.addChild(result, 'spec'); + + if (result.status !== 'excluded') { + this.specsExecuted++; + } + + if (result.status === 'failed') { + this.failureCount++; + } + + if (result.status == 'pending') { + this.pendingSpecCount++; + } + }; + + ResultsStateBuilder.prototype.jasmineDone = function(result) { + if (result.failedExpectations) { + this.failureCount += result.failedExpectations.length; + } + }; + + function HtmlReporter(options) { + function config() { + return (options.env && options.env.configuration()) || {}; + } + + const getContainer = options.getContainer; + const createElement = options.createElement; + const createTextNode = options.createTextNode; + const navigateWithNewParam = options.navigateWithNewParam || function() {}; + const addToExistingQueryString = + options.addToExistingQueryString || defaultQueryString; + const filterSpecs = options.filterSpecs; + let htmlReporterMain; + let symbols; + const deprecationWarnings = []; + const failures = []; + + this.initialize = function() { + clearPrior(); + htmlReporterMain = createDom( + 'div', + { className: 'jasmine_html-reporter' }, + createDom( + 'div', + { className: 'jasmine-banner' }, + createDom('a', { + className: 'jasmine-title', + href: 'http://jasmine.github.io/', + target: '_blank' + }), + createDom('span', { className: 'jasmine-version' }, j$.version) + ), + createDom('ul', { className: 'jasmine-symbol-summary' }), + createDom('div', { className: 'jasmine-alert' }), + createDom( + 'div', + { className: 'jasmine-results' }, + createDom('div', { className: 'jasmine-failures' }) + ) + ); + getContainer().appendChild(htmlReporterMain); + }; + + let totalSpecsDefined; + this.jasmineStarted = function(options) { + totalSpecsDefined = options.totalSpecsDefined || 0; + }; + + const summary = createDom('div', { className: 'jasmine-summary' }); + + const stateBuilder = new ResultsStateBuilder(); + + this.suiteStarted = function(result) { + stateBuilder.suiteStarted(result); + }; + + this.suiteDone = function(result) { + stateBuilder.suiteDone(result); + + if (result.status === 'failed') { + failures.push(failureDom(result)); + } + addDeprecationWarnings(result, 'suite'); + }; + + this.specStarted = function(result) { + stateBuilder.specStarted(result); + }; + + this.specDone = function(result) { + stateBuilder.specDone(result); + + if (noExpectations(result)) { + const noSpecMsg = "Spec '" + result.fullName + "' has no expectations."; + if (result.status === 'failed') { + console.error(noSpecMsg); + } else { + console.warn(noSpecMsg); + } + } + + if (!symbols) { + symbols = find('.jasmine-symbol-summary'); + } + + symbols.appendChild( + createDom('li', { + className: this.displaySpecInCorrectFormat(result), + id: 'spec_' + result.id, + title: result.fullName + }) + ); + + if (result.status === 'failed') { + failures.push(failureDom(result)); + } + + addDeprecationWarnings(result, 'spec'); + }; + + this.displaySpecInCorrectFormat = function(result) { + return noExpectations(result) && result.status === 'passed' + ? 'jasmine-empty' + : this.resultStatus(result.status); + }; + + this.resultStatus = function(status) { + if (status === 'excluded') { + return config().hideDisabled + ? 'jasmine-excluded-no-display' + : 'jasmine-excluded'; + } + return 'jasmine-' + status; + }; + + this.jasmineDone = function(doneResult) { + stateBuilder.jasmineDone(doneResult); + const banner = find('.jasmine-banner'); + const alert = find('.jasmine-alert'); + const order = doneResult && doneResult.order; + + alert.appendChild( + createDom( + 'span', + { className: 'jasmine-duration' }, + 'finished in ' + doneResult.totalTime / 1000 + 's' + ) + ); + + banner.appendChild(optionsMenu(config())); + + if (stateBuilder.specsExecuted < totalSpecsDefined) { + const skippedMessage = + 'Ran ' + + stateBuilder.specsExecuted + + ' of ' + + totalSpecsDefined + + ' specs - run all'; + // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + const skippedLink = + (window.location.pathname || '') + + addToExistingQueryString('spec', ''); + alert.appendChild( + createDom( + 'span', + { className: 'jasmine-bar jasmine-skipped' }, + createDom( + 'a', + { href: skippedLink, title: 'Run all specs' }, + skippedMessage + ) + ) + ); + } + let statusBarMessage = ''; + let statusBarClassName = 'jasmine-overall-result jasmine-bar '; + const globalFailures = + (doneResult && doneResult.failedExpectations) || []; + const failed = stateBuilder.failureCount + globalFailures.length > 0; + + if (totalSpecsDefined > 0 || failed) { + statusBarMessage += + pluralize('spec', stateBuilder.specsExecuted) + + ', ' + + pluralize('failure', stateBuilder.failureCount); + if (stateBuilder.pendingSpecCount) { + statusBarMessage += + ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount); + } + } + + if (doneResult.overallStatus === 'passed') { + statusBarClassName += ' jasmine-passed '; + } else if (doneResult.overallStatus === 'incomplete') { + statusBarClassName += ' jasmine-incomplete '; + statusBarMessage = + 'Incomplete: ' + + doneResult.incompleteReason + + ', ' + + statusBarMessage; + } else { + statusBarClassName += ' jasmine-failed '; + } + + let seedBar; + if (order && order.random) { + seedBar = createDom( + 'span', + { className: 'jasmine-seed-bar' }, + ', randomized with seed ', + createDom( + 'a', + { + title: 'randomized with seed ' + order.seed, + href: seedHref(order.seed) + }, + order.seed + ) + ); + } + + alert.appendChild( + createDom( + 'span', + { className: statusBarClassName }, + statusBarMessage, + seedBar + ) + ); + + const errorBarClassName = 'jasmine-bar jasmine-errored'; + const afterAllMessagePrefix = 'AfterAll '; + + for (let i = 0; i < globalFailures.length; i++) { + alert.appendChild( + createDom( + 'span', + { className: errorBarClassName }, + globalFailureMessage(globalFailures[i]) + ) + ); + } + + function globalFailureMessage(failure) { + if (failure.globalErrorType === 'load') { + const prefix = 'Error during loading: ' + failure.message; + + if (failure.filename) { + return ( + prefix + ' in ' + failure.filename + ' line ' + failure.lineno + ); + } else { + return prefix; + } + } else if (failure.globalErrorType === 'afterAll') { + return afterAllMessagePrefix + failure.message; + } else { + return failure.message; + } + } + + addDeprecationWarnings(doneResult); + + for (let i = 0; i < deprecationWarnings.length; i++) { + const children = []; + let context; + + switch (deprecationWarnings[i].runnableType) { + case 'spec': + context = '(in spec: ' + deprecationWarnings[i].runnableName + ')'; + break; + case 'suite': + context = '(in suite: ' + deprecationWarnings[i].runnableName + ')'; + break; + default: + context = ''; + } + + deprecationWarnings[i].message.split('\n').forEach(function(line) { + children.push(line); + children.push(createDom('br')); + }); + + children[0] = 'DEPRECATION: ' + children[0]; + children.push(context); + + if (deprecationWarnings[i].stack) { + children.push(createExpander(deprecationWarnings[i].stack)); + } + + alert.appendChild( + createDom( + 'span', + { className: 'jasmine-bar jasmine-warning' }, + children + ) + ); + } + + const results = find('.jasmine-results'); + results.appendChild(summary); + + summaryList(stateBuilder.topResults, summary); + + if (failures.length) { + alert.appendChild( + createDom( + 'span', + { className: 'jasmine-menu jasmine-bar jasmine-spec-list' }, + createDom('span', {}, 'Spec List | '), + createDom( + 'a', + { className: 'jasmine-failures-menu', href: '#' }, + 'Failures' + ) + ) + ); + alert.appendChild( + createDom( + 'span', + { className: 'jasmine-menu jasmine-bar jasmine-failure-list' }, + createDom( + 'a', + { className: 'jasmine-spec-list-menu', href: '#' }, + 'Spec List' + ), + createDom('span', {}, ' | Failures ') + ) + ); + + find('.jasmine-failures-menu').onclick = function() { + setMenuModeTo('jasmine-failure-list'); + return false; + }; + find('.jasmine-spec-list-menu').onclick = function() { + setMenuModeTo('jasmine-spec-list'); + return false; + }; + + setMenuModeTo('jasmine-failure-list'); + + const failureNode = find('.jasmine-failures'); + for (let i = 0; i < failures.length; i++) { + failureNode.appendChild(failures[i]); + } + } + }; + + return this; + + function failureDom(result) { + const failure = createDom( + 'div', + { className: 'jasmine-spec-detail jasmine-failed' }, + failureDescription(result, stateBuilder.currentParent), + createDom('div', { className: 'jasmine-messages' }) + ); + const messages = failure.childNodes[1]; + + for (let i = 0; i < result.failedExpectations.length; i++) { + const expectation = result.failedExpectations[i]; + messages.appendChild( + createDom( + 'div', + { className: 'jasmine-result-message' }, + expectation.message + ) + ); + messages.appendChild( + createDom( + 'div', + { className: 'jasmine-stack-trace' }, + expectation.stack + ) + ); + } + + if (result.failedExpectations.length === 0) { + messages.appendChild( + createDom( + 'div', + { className: 'jasmine-result-message' }, + 'Spec has no expectations' + ) + ); + } + + if (result.debugLogs) { + messages.appendChild(debugLogTable(result.debugLogs)); + } + + return failure; + } + + function debugLogTable(debugLogs) { + const tbody = createDom('tbody'); + + debugLogs.forEach(function(entry) { + tbody.appendChild( + createDom( + 'tr', + {}, + createDom('td', {}, entry.timestamp.toString()), + createDom('td', {}, entry.message) + ) + ); + }); + + return createDom( + 'div', + { className: 'jasmine-debug-log' }, + createDom( + 'div', + { className: 'jasmine-debug-log-header' }, + 'Debug logs' + ), + createDom( + 'table', + {}, + createDom( + 'thead', + {}, + createDom( + 'tr', + {}, + createDom('th', {}, 'Time (ms)'), + createDom('th', {}, 'Message') + ) + ), + tbody + ) + ); + } + + function summaryList(resultsTree, domParent) { + let specListNode; + for (let i = 0; i < resultsTree.children.length; i++) { + const resultNode = resultsTree.children[i]; + if (filterSpecs && !hasActiveSpec(resultNode)) { + continue; + } + if (resultNode.type === 'suite') { + const suiteListNode = createDom( + 'ul', + { className: 'jasmine-suite', id: 'suite-' + resultNode.result.id }, + createDom( + 'li', + { + className: + 'jasmine-suite-detail jasmine-' + resultNode.result.status + }, + createDom( + 'a', + { href: specHref(resultNode.result) }, + resultNode.result.description + ) + ) + ); + + summaryList(resultNode, suiteListNode); + domParent.appendChild(suiteListNode); + } + if (resultNode.type === 'spec') { + if (domParent.getAttribute('class') !== 'jasmine-specs') { + specListNode = createDom('ul', { className: 'jasmine-specs' }); + domParent.appendChild(specListNode); + } + let specDescription = resultNode.result.description; + if (noExpectations(resultNode.result)) { + specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; + } + if (resultNode.result.status === 'pending') { + if (resultNode.result.pendingReason !== '') { + specDescription += + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; + } else { + specDescription += ' PENDING'; + } + } + specListNode.appendChild( + createDom( + 'li', + { + className: 'jasmine-' + resultNode.result.status, + id: 'spec-' + resultNode.result.id + }, + createDom( + 'a', + { href: specHref(resultNode.result) }, + specDescription + ) + ) + ); + } + } + } + + function optionsMenu(config) { + const optionsMenuDom = createDom( + 'div', + { className: 'jasmine-run-options' }, + createDom('span', { className: 'jasmine-trigger' }, 'Options'), + createDom( + 'div', + { className: 'jasmine-payload' }, + createDom( + 'div', + { className: 'jasmine-stop-on-failure' }, + createDom('input', { + className: 'jasmine-fail-fast', + id: 'jasmine-fail-fast', + type: 'checkbox' + }), + createDom( + 'label', + { className: 'jasmine-label', for: 'jasmine-fail-fast' }, + 'stop execution on spec failure' + ) + ), + createDom( + 'div', + { className: 'jasmine-throw-failures' }, + createDom('input', { + className: 'jasmine-throw', + id: 'jasmine-throw-failures', + type: 'checkbox' + }), + createDom( + 'label', + { className: 'jasmine-label', for: 'jasmine-throw-failures' }, + 'stop spec on expectation failure' + ) + ), + createDom( + 'div', + { className: 'jasmine-random-order' }, + createDom('input', { + className: 'jasmine-random', + id: 'jasmine-random-order', + type: 'checkbox' + }), + createDom( + 'label', + { className: 'jasmine-label', for: 'jasmine-random-order' }, + 'run tests in random order' + ) + ), + createDom( + 'div', + { className: 'jasmine-hide-disabled' }, + createDom('input', { + className: 'jasmine-disabled', + id: 'jasmine-hide-disabled', + type: 'checkbox' + }), + createDom( + 'label', + { className: 'jasmine-label', for: 'jasmine-hide-disabled' }, + 'hide disabled tests' + ) + ) + ) + ); + + const failFastCheckbox = optionsMenuDom.querySelector( + '#jasmine-fail-fast' + ); + failFastCheckbox.checked = config.stopOnSpecFailure; + failFastCheckbox.onclick = function() { + navigateWithNewParam('stopOnSpecFailure', !config.stopOnSpecFailure); + }; + + const throwCheckbox = optionsMenuDom.querySelector( + '#jasmine-throw-failures' + ); + throwCheckbox.checked = config.stopSpecOnExpectationFailure; + throwCheckbox.onclick = function() { + navigateWithNewParam( + 'stopSpecOnExpectationFailure', + !config.stopSpecOnExpectationFailure + ); + }; + + const randomCheckbox = optionsMenuDom.querySelector( + '#jasmine-random-order' + ); + randomCheckbox.checked = config.random; + randomCheckbox.onclick = function() { + navigateWithNewParam('random', !config.random); + }; + + const hideDisabled = optionsMenuDom.querySelector( + '#jasmine-hide-disabled' + ); + hideDisabled.checked = config.hideDisabled; + hideDisabled.onclick = function() { + navigateWithNewParam('hideDisabled', !config.hideDisabled); + }; + + const optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'), + optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'), + isOpen = /\bjasmine-open\b/; + + optionsTrigger.onclick = function() { + if (isOpen.test(optionsPayload.className)) { + optionsPayload.className = optionsPayload.className.replace( + isOpen, + '' + ); + } else { + optionsPayload.className += ' jasmine-open'; + } + }; + + return optionsMenuDom; + } + + function failureDescription(result, suite) { + const wrapper = createDom( + 'div', + { className: 'jasmine-description' }, + createDom( + 'a', + { title: result.description, href: specHref(result) }, + result.description + ) + ); + let suiteLink; + + while (suite && suite.parent) { + wrapper.insertBefore(createTextNode(' > '), wrapper.firstChild); + suiteLink = createDom( + 'a', + { href: suiteHref(suite) }, + suite.result.description + ); + wrapper.insertBefore(suiteLink, wrapper.firstChild); + + suite = suite.parent; + } + + return wrapper; + } + + function suiteHref(suite) { + const els = []; + + while (suite && suite.parent) { + els.unshift(suite.result.description); + suite = suite.parent; + } + + // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + return ( + (window.location.pathname || '') + + addToExistingQueryString('spec', els.join(' ')) + ); + } + + function addDeprecationWarnings(result, runnableType) { + if (result && result.deprecationWarnings) { + for (let i = 0; i < result.deprecationWarnings.length; i++) { + const warning = result.deprecationWarnings[i].message; + deprecationWarnings.push({ + message: warning, + stack: result.deprecationWarnings[i].stack, + runnableName: result.fullName, + runnableType: runnableType + }); + } + } + } + + function createExpander(stackTrace) { + const expandLink = createDom('a', { href: '#' }, 'Show stack trace'); + const root = createDom( + 'div', + { className: 'jasmine-expander' }, + expandLink, + createDom( + 'div', + { className: 'jasmine-expander-contents jasmine-stack-trace' }, + stackTrace + ) + ); + + expandLink.addEventListener('click', function(e) { + e.preventDefault(); + + if (root.classList.contains('jasmine-expanded')) { + root.classList.remove('jasmine-expanded'); + expandLink.textContent = 'Show stack trace'; + } else { + root.classList.add('jasmine-expanded'); + expandLink.textContent = 'Hide stack trace'; + } + }); + + return root; + } + + function find(selector) { + return getContainer().querySelector('.jasmine_html-reporter ' + selector); + } + + function clearPrior() { + const oldReporter = find(''); + + if (oldReporter) { + getContainer().removeChild(oldReporter); + } + } + + function createDom(type, attrs, childrenArrayOrVarArgs) { + const el = createElement(type); + let children; + + if (j$.isArray_(childrenArrayOrVarArgs)) { + children = childrenArrayOrVarArgs; + } else { + children = []; + + for (let i = 2; i < arguments.length; i++) { + children.push(arguments[i]); + } + } + + for (let i = 0; i < children.length; i++) { + const child = children[i]; + + if (typeof child === 'string') { + el.appendChild(createTextNode(child)); + } else { + if (child) { + el.appendChild(child); + } + } + } + + for (const attr in attrs) { + if (attr == 'className') { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; + } + + function pluralize(singular, count) { + const word = count == 1 ? singular : singular + 's'; + + return '' + count + ' ' + word; + } + + function specHref(result) { + // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + return ( + (window.location.pathname || '') + + addToExistingQueryString('spec', result.fullName) + ); + } + + function seedHref(seed) { + // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 + return ( + (window.location.pathname || '') + + addToExistingQueryString('seed', seed) + ); + } + + function defaultQueryString(key, value) { + return '?' + key + '=' + value; + } + + function setMenuModeTo(mode) { + htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode); + } + + function noExpectations(result) { + const allExpectations = + result.failedExpectations.length + result.passedExpectations.length; + + return ( + allExpectations === 0 && + (result.status === 'passed' || result.status === 'failed') + ); + } + + function hasActiveSpec(resultNode) { + if (resultNode.type == 'spec' && resultNode.result.status != 'excluded') { + return true; + } + + if (resultNode.type == 'suite') { + for (let i = 0, j = resultNode.children.length; i < j; i++) { + if (hasActiveSpec(resultNode.children[i])) { + return true; + } + } + } + } + } + + return HtmlReporter; +}; + +jasmineRequire.HtmlSpecFilter = function() { + function HtmlSpecFilter(options) { + const filterString = + options && + options.filterString() && + options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + const filterPattern = new RegExp(filterString); + + this.matches = function(specName) { + return filterPattern.test(specName); + }; + } + + return HtmlSpecFilter; +}; + +jasmineRequire.ResultsNode = function() { + function ResultsNode(result, type, parent) { + this.result = result; + this.type = type; + this.parent = parent; + + this.children = []; + + this.addChild = function(result, type) { + this.children.push(new ResultsNode(result, type, this)); + }; + + this.last = function() { + return this.children[this.children.length - 1]; + }; + + this.updateResult = function(result) { + this.result = result; + }; + } + + return ResultsNode; +}; + +jasmineRequire.QueryString = function() { + function QueryString(options) { + this.navigateWithNewParam = function(key, value) { + options.getWindowLocation().search = this.fullStringWithNewParam( + key, + value + ); + }; + + this.fullStringWithNewParam = function(key, value) { + const paramMap = queryStringToParamMap(); + paramMap[key] = value; + return toQueryString(paramMap); + }; + + this.getParam = function(key) { + return queryStringToParamMap()[key]; + }; + + return this; + + function toQueryString(paramMap) { + const qStrPairs = []; + for (const prop in paramMap) { + qStrPairs.push( + encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]) + ); + } + return '?' + qStrPairs.join('&'); + } + + function queryStringToParamMap() { + const paramStr = options.getWindowLocation().search.substring(1); + let params = []; + const paramMap = {}; + + if (paramStr.length > 0) { + params = paramStr.split('&'); + for (let i = 0; i < params.length; i++) { + const p = params[i].split('='); + let value = decodeURIComponent(p[1]); + if (value === 'true' || value === 'false') { + value = JSON.parse(value); + } + paramMap[decodeURIComponent(p[0])] = value; + } + } + + return paramMap; + } + } + + return QueryString; +}; diff --git a/tests/js/lib/jasmine-5.1.2/jasmine.css b/tests/js/lib/jasmine-5.1.2/jasmine.css new file mode 100644 index 0000000..a19204a --- /dev/null +++ b/tests/js/lib/jasmine-5.1.2/jasmine.css @@ -0,0 +1,298 @@ +@charset "UTF-8"; +body { + overflow-y: scroll; +} + +.jasmine_html-reporter { + width: 100%; + background-color: #eee; + padding: 5px; + margin: -8px; + font-size: 11px; + font-family: Monaco, "Lucida Console", monospace; + line-height: 14px; + color: #333; +} +.jasmine_html-reporter a { + text-decoration: none; +} +.jasmine_html-reporter a:hover { + text-decoration: underline; +} +.jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { + margin: 0; + line-height: 14px; +} +.jasmine_html-reporter .jasmine-banner, +.jasmine_html-reporter .jasmine-symbol-summary, +.jasmine_html-reporter .jasmine-summary, +.jasmine_html-reporter .jasmine-result-message, +.jasmine_html-reporter .jasmine-spec .jasmine-description, +.jasmine_html-reporter .jasmine-spec-detail .jasmine-description, +.jasmine_html-reporter .jasmine-alert .jasmine-bar, +.jasmine_html-reporter .jasmine-stack-trace { + padding-left: 9px; + padding-right: 9px; +} +.jasmine_html-reporter .jasmine-banner { + position: relative; +} +.jasmine_html-reporter .jasmine-banner .jasmine-title { + background: url("data:image/png;base64,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") no-repeat; + background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=") no-repeat, none; + background-size: 100%; + display: block; + float: left; + width: 90px; + height: 25px; +} +.jasmine_html-reporter .jasmine-banner .jasmine-version { + margin-left: 14px; + position: relative; + top: 6px; +} +.jasmine_html-reporter #jasmine_content { + position: fixed; + right: 100%; +} +.jasmine_html-reporter .jasmine-banner { + margin-top: 14px; +} +.jasmine_html-reporter .jasmine-duration { + color: #fff; + float: right; + line-height: 28px; + padding-right: 9px; +} +.jasmine_html-reporter .jasmine-symbol-summary { + overflow: hidden; + margin: 14px 0; +} +.jasmine_html-reporter .jasmine-symbol-summary li { + display: inline-block; + height: 10px; + width: 14px; + font-size: 16px; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed { + font-size: 14px; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed:before { + color: #007069; + content: "•"; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed { + line-height: 9px; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed:before { + color: #ca3a11; + content: "×"; + font-weight: bold; + margin-left: -1px; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded { + font-size: 14px; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded:before { + color: #bababa; + content: "•"; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-excluded-no-display { + font-size: 14px; + display: none; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending { + line-height: 17px; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending:before { + color: #ba9d37; + content: "*"; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty { + font-size: 14px; +} +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty:before { + color: #ba9d37; + content: "•"; +} +.jasmine_html-reporter .jasmine-run-options { + float: right; + margin-right: 5px; + border: 1px solid #8a4182; + color: #8a4182; + position: relative; + line-height: 20px; +} +.jasmine_html-reporter .jasmine-run-options .jasmine-trigger { + cursor: pointer; + padding: 8px 16px; +} +.jasmine_html-reporter .jasmine-run-options .jasmine-payload { + position: absolute; + display: none; + right: -1px; + border: 1px solid #8a4182; + background-color: #eee; + white-space: nowrap; + padding: 4px 8px; +} +.jasmine_html-reporter .jasmine-run-options .jasmine-payload.jasmine-open { + display: block; +} +.jasmine_html-reporter .jasmine-bar { + line-height: 28px; + font-size: 14px; + display: block; + color: #eee; +} +.jasmine_html-reporter .jasmine-bar.jasmine-failed, .jasmine_html-reporter .jasmine-bar.jasmine-errored { + background-color: #ca3a11; + border-bottom: 1px solid #eee; +} +.jasmine_html-reporter .jasmine-bar.jasmine-passed { + background-color: #007069; +} +.jasmine_html-reporter .jasmine-bar.jasmine-incomplete { + background-color: #bababa; +} +.jasmine_html-reporter .jasmine-bar.jasmine-skipped { + background-color: #bababa; +} +.jasmine_html-reporter .jasmine-bar.jasmine-warning { + margin-top: 14px; + margin-bottom: 14px; + background-color: #ba9d37; + color: #333; +} +.jasmine_html-reporter .jasmine-bar.jasmine-menu { + background-color: #fff; + color: #000; +} +.jasmine_html-reporter .jasmine-bar.jasmine-menu a { + color: blue; + text-decoration: underline; +} +.jasmine_html-reporter .jasmine-bar a { + color: white; +} +.jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list, +.jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures { + display: none; +} +.jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list, +.jasmine_html-reporter.jasmine-failure-list .jasmine-summary { + display: none; +} +.jasmine_html-reporter .jasmine-results { + margin-top: 14px; +} +.jasmine_html-reporter .jasmine-summary { + margin-top: 14px; +} +.jasmine_html-reporter .jasmine-summary ul { + list-style-type: none; + margin-left: 14px; + padding-top: 0; + padding-left: 0; +} +.jasmine_html-reporter .jasmine-summary ul.jasmine-suite { + margin-top: 7px; + margin-bottom: 7px; +} +.jasmine_html-reporter .jasmine-summary li.jasmine-passed a { + color: #007069; +} +.jasmine_html-reporter .jasmine-summary li.jasmine-failed a { + color: #ca3a11; +} +.jasmine_html-reporter .jasmine-summary li.jasmine-empty a { + color: #ba9d37; +} +.jasmine_html-reporter .jasmine-summary li.jasmine-pending a { + color: #ba9d37; +} +.jasmine_html-reporter .jasmine-summary li.jasmine-excluded a { + color: #bababa; +} +.jasmine_html-reporter .jasmine-specs li.jasmine-passed a:before { + content: "• "; +} +.jasmine_html-reporter .jasmine-specs li.jasmine-failed a:before { + content: "× "; +} +.jasmine_html-reporter .jasmine-specs li.jasmine-empty a:before { + content: "* "; +} +.jasmine_html-reporter .jasmine-specs li.jasmine-pending a:before { + content: "• "; +} +.jasmine_html-reporter .jasmine-specs li.jasmine-excluded a:before { + content: "• "; +} +.jasmine_html-reporter .jasmine-description + .jasmine-suite { + margin-top: 0; +} +.jasmine_html-reporter .jasmine-suite { + margin-top: 14px; +} +.jasmine_html-reporter .jasmine-suite a { + color: #333; +} +.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail { + margin-bottom: 28px; +} +.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description { + background-color: #ca3a11; + color: white; +} +.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description a { + color: white; +} +.jasmine_html-reporter .jasmine-result-message { + padding-top: 14px; + color: #333; + white-space: pre-wrap; +} +.jasmine_html-reporter .jasmine-result-message span.jasmine-result { + display: block; +} +.jasmine_html-reporter .jasmine-stack-trace { + margin: 5px 0 0 0; + max-height: 224px; + overflow: auto; + line-height: 18px; + color: #666; + border: 1px solid #ddd; + background: white; + white-space: pre; +} +.jasmine_html-reporter .jasmine-expander a { + display: block; + margin-left: 14px; + color: blue; + text-decoration: underline; +} +.jasmine_html-reporter .jasmine-expander-contents { + display: none; +} +.jasmine_html-reporter .jasmine-expanded { + padding-bottom: 10px; +} +.jasmine_html-reporter .jasmine-expanded .jasmine-expander-contents { + display: block; + margin-left: 14px; + padding: 5px; +} +.jasmine_html-reporter .jasmine-debug-log { + margin: 5px 0 0 0; + padding: 5px; + color: #666; + border: 1px solid #ddd; + background: white; +} +.jasmine_html-reporter .jasmine-debug-log table { + border-spacing: 0; +} +.jasmine_html-reporter .jasmine-debug-log table, .jasmine_html-reporter .jasmine-debug-log th, .jasmine_html-reporter .jasmine-debug-log td { + border: 1px solid #ddd; +} diff --git a/tests/js/lib/jasmine-5.1.2/jasmine.js b/tests/js/lib/jasmine-5.1.2/jasmine.js new file mode 100644 index 0000000..1378b62 --- /dev/null +++ b/tests/js/lib/jasmine-5.1.2/jasmine.js @@ -0,0 +1,10817 @@ +/* +Copyright (c) 2008-2019 Pivotal Labs +Copyright (c) 2008-2024 The Jasmine developers + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +// eslint-disable-next-line no-unused-vars,no-var +var getJasmineRequireObj = (function(jasmineGlobal) { + let jasmineRequire; + + if ( + typeof module !== 'undefined' && + module.exports && + typeof exports !== 'undefined' + ) { + if (typeof global !== 'undefined') { + jasmineGlobal = global; + } else { + jasmineGlobal = {}; + } + jasmineRequire = exports; + } else { + if ( + typeof window !== 'undefined' && + typeof window.toString === 'function' && + window.toString() === '[object GjsGlobal]' + ) { + jasmineGlobal = window; + } + jasmineRequire = jasmineGlobal.jasmineRequire = {}; + } + + function getJasmineRequire() { + return jasmineRequire; + } + + getJasmineRequire().core = function(jRequire) { + const j$ = {}; + + jRequire.base(j$, jasmineGlobal); + j$.util = jRequire.util(j$); + j$.errors = jRequire.errors(); + j$.formatErrorMsg = jRequire.formatErrorMsg(); + j$.Any = jRequire.Any(j$); + j$.Anything = jRequire.Anything(j$); + j$.CallTracker = jRequire.CallTracker(j$); + j$.MockDate = jRequire.MockDate(j$); + j$.getClearStack = jRequire.clearStack(j$); + j$.Clock = jRequire.Clock(); + j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(j$); + j$.Deprecator = jRequire.Deprecator(j$); + j$.Env = jRequire.Env(j$); + j$.StackTrace = jRequire.StackTrace(j$); + j$.ExceptionFormatter = jRequire.ExceptionFormatter(j$); + j$.ExpectationFilterChain = jRequire.ExpectationFilterChain(); + j$.Expector = jRequire.Expector(j$); + j$.Expectation = jRequire.Expectation(j$); + j$.buildExpectationResult = jRequire.buildExpectationResult(j$); + j$.JsApiReporter = jRequire.JsApiReporter(j$); + j$.makePrettyPrinter = jRequire.makePrettyPrinter(j$); + j$.basicPrettyPrinter_ = j$.makePrettyPrinter(); + j$.MatchersUtil = jRequire.MatchersUtil(j$); + j$.ObjectContaining = jRequire.ObjectContaining(j$); + j$.ArrayContaining = jRequire.ArrayContaining(j$); + j$.ArrayWithExactContents = jRequire.ArrayWithExactContents(j$); + j$.MapContaining = jRequire.MapContaining(j$); + j$.SetContaining = jRequire.SetContaining(j$); + j$.QueueRunner = jRequire.QueueRunner(j$); + j$.NeverSkipPolicy = jRequire.NeverSkipPolicy(j$); + j$.SkipAfterBeforeAllErrorPolicy = jRequire.SkipAfterBeforeAllErrorPolicy( + j$ + ); + j$.CompleteOnFirstErrorSkipPolicy = jRequire.CompleteOnFirstErrorSkipPolicy( + j$ + ); + j$.reporterEvents = jRequire.reporterEvents(j$); + j$.ReportDispatcher = jRequire.ReportDispatcher(j$); + j$.ParallelReportDispatcher = jRequire.ParallelReportDispatcher(j$); + j$.RunableResources = jRequire.RunableResources(j$); + j$.Runner = jRequire.Runner(j$); + j$.Spec = jRequire.Spec(j$); + j$.Spy = jRequire.Spy(j$); + j$.SpyFactory = jRequire.SpyFactory(j$); + j$.SpyRegistry = jRequire.SpyRegistry(j$); + j$.SpyStrategy = jRequire.SpyStrategy(j$); + j$.StringMatching = jRequire.StringMatching(j$); + j$.StringContaining = jRequire.StringContaining(j$); + j$.UserContext = jRequire.UserContext(j$); + j$.Suite = jRequire.Suite(j$); + j$.SuiteBuilder = jRequire.SuiteBuilder(j$); + j$.Timer = jRequire.Timer(); + j$.TreeProcessor = jRequire.TreeProcessor(); + j$.version = jRequire.version(); + j$.Order = jRequire.Order(); + j$.DiffBuilder = jRequire.DiffBuilder(j$); + j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$); + j$.ObjectPath = jRequire.ObjectPath(j$); + j$.MismatchTree = jRequire.MismatchTree(j$); + j$.GlobalErrors = jRequire.GlobalErrors(j$); + + j$.Truthy = jRequire.Truthy(j$); + j$.Falsy = jRequire.Falsy(j$); + j$.Empty = jRequire.Empty(j$); + j$.NotEmpty = jRequire.NotEmpty(j$); + j$.Is = jRequire.Is(j$); + + j$.matchers = jRequire.requireMatchers(jRequire, j$); + j$.asyncMatchers = jRequire.requireAsyncMatchers(jRequire, j$); + + return j$; + }; + + return getJasmineRequire; +})(this); + +getJasmineRequireObj().requireMatchers = function(jRequire, j$) { + const availableMatchers = [ + 'nothing', + 'toBe', + 'toBeCloseTo', + 'toBeDefined', + 'toBeInstanceOf', + 'toBeFalse', + 'toBeFalsy', + 'toBeGreaterThan', + 'toBeGreaterThanOrEqual', + 'toBeLessThan', + 'toBeLessThanOrEqual', + 'toBeNaN', + 'toBeNegativeInfinity', + 'toBeNull', + 'toBePositiveInfinity', + 'toBeTrue', + 'toBeTruthy', + 'toBeUndefined', + 'toContain', + 'toEqual', + 'toHaveSize', + 'toHaveBeenCalled', + 'toHaveBeenCalledBefore', + 'toHaveBeenCalledOnceWith', + 'toHaveBeenCalledTimes', + 'toHaveBeenCalledWith', + 'toHaveClass', + 'toHaveSpyInteractions', + 'toMatch', + 'toThrow', + 'toThrowError', + 'toThrowMatching' + ], + matchers = {}; + + for (const name of availableMatchers) { + matchers[name] = jRequire[name](j$); + } + + return matchers; +}; + +getJasmineRequireObj().base = function(j$, jasmineGlobal) { + /** + * Maximum object depth the pretty printer will print to. + * Set this to a lower value to speed up pretty printing if you have large objects. + * @name jasmine.MAX_PRETTY_PRINT_DEPTH + * @default 8 + * @since 1.3.0 + */ + j$.MAX_PRETTY_PRINT_DEPTH = 8; + /** + * Maximum number of array elements to display when pretty printing objects. + * This will also limit the number of keys and values displayed for an object. + * Elements past this number will be ellipised. + * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH + * @default 50 + * @since 2.7.0 + */ + j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 50; + /** + * Maximum number of characters to display when pretty printing objects. + * Characters past this number will be ellipised. + * @name jasmine.MAX_PRETTY_PRINT_CHARS + * @default 100 + * @since 2.9.0 + */ + j$.MAX_PRETTY_PRINT_CHARS = 1000; + /** + * Default number of milliseconds Jasmine will wait for an asynchronous spec, + * before, or after function to complete. This can be overridden on a case by + * case basis by passing a time limit as the third argument to {@link it}, + * {@link beforeEach}, {@link afterEach}, {@link beforeAll}, or + * {@link afterAll}. The value must be no greater than the largest number of + * milliseconds supported by setTimeout, which is usually 2147483647. + * + * While debugging tests, you may want to set this to a large number (or pass + * a large number to one of the functions mentioned above) so that Jasmine + * does not move on to after functions or the next spec while you're debugging. + * @name jasmine.DEFAULT_TIMEOUT_INTERVAL + * @default 5000 + * @since 1.3.0 + */ + let DEFAULT_TIMEOUT_INTERVAL = 5000; + Object.defineProperty(j$, 'DEFAULT_TIMEOUT_INTERVAL', { + get: function() { + return DEFAULT_TIMEOUT_INTERVAL; + }, + set: function(newValue) { + j$.util.validateTimeout(newValue, 'jasmine.DEFAULT_TIMEOUT_INTERVAL'); + DEFAULT_TIMEOUT_INTERVAL = newValue; + } + }); + + j$.getGlobal = function() { + return jasmineGlobal; + }; + + /** + * Get the currently booted Jasmine Environment. + * + * @name jasmine.getEnv + * @since 1.3.0 + * @function + * @return {Env} + */ + j$.getEnv = function(options) { + const env = (j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options)); + //jasmine. singletons in here (setTimeout blah blah). + return env; + }; + + j$.isArray_ = function(value) { + return j$.isA_('Array', value); + }; + + j$.isObject_ = function(value) { + return ( + !j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value) + ); + }; + + j$.isString_ = function(value) { + return j$.isA_('String', value); + }; + + j$.isNumber_ = function(value) { + return j$.isA_('Number', value); + }; + + j$.isFunction_ = function(value) { + return j$.isA_('Function', value); + }; + + j$.isAsyncFunction_ = function(value) { + return j$.isA_('AsyncFunction', value); + }; + + j$.isGeneratorFunction_ = function(value) { + return j$.isA_('GeneratorFunction', value); + }; + + j$.isTypedArray_ = function(value) { + return ( + j$.isA_('Float32Array', value) || + j$.isA_('Float64Array', value) || + j$.isA_('Int16Array', value) || + j$.isA_('Int32Array', value) || + j$.isA_('Int8Array', value) || + j$.isA_('Uint16Array', value) || + j$.isA_('Uint32Array', value) || + j$.isA_('Uint8Array', value) || + j$.isA_('Uint8ClampedArray', value) + ); + }; + + j$.isA_ = function(typeName, value) { + return j$.getType_(value) === '[object ' + typeName + ']'; + }; + + j$.isError_ = function(value) { + if (!value) { + return false; + } + + if (value instanceof Error) { + return true; + } + + return typeof value.stack === 'string' && typeof value.message === 'string'; + }; + + j$.isAsymmetricEqualityTester_ = function(obj) { + return obj ? j$.isA_('Function', obj.asymmetricMatch) : false; + }; + + j$.getType_ = function(value) { + return Object.prototype.toString.apply(value); + }; + + j$.isDomNode = function(obj) { + // Node is a function, because constructors + return typeof jasmineGlobal.Node !== 'undefined' + ? obj instanceof jasmineGlobal.Node + : obj !== null && + typeof obj === 'object' && + typeof obj.nodeType === 'number' && + typeof obj.nodeName === 'string'; + // return obj.nodeType > 0; + }; + + j$.isMap = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === jasmineGlobal.Map + ); + }; + + j$.isSet = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === jasmineGlobal.Set + ); + }; + + j$.isWeakMap = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === jasmineGlobal.WeakMap + ); + }; + + j$.isURL = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === jasmineGlobal.URL + ); + }; + + j$.isIterable_ = function(value) { + return value && !!value[Symbol.iterator]; + }; + + j$.isDataView = function(obj) { + return ( + obj !== null && + typeof obj !== 'undefined' && + obj.constructor === jasmineGlobal.DataView + ); + }; + + j$.isPromise = function(obj) { + return !!obj && obj.constructor === jasmineGlobal.Promise; + }; + + j$.isPromiseLike = function(obj) { + return !!obj && j$.isFunction_(obj.then); + }; + + j$.fnNameFor = function(func) { + if (func.name) { + return func.name; + } + + const matches = + func.toString().match(/^\s*function\s*(\w+)\s*\(/) || + func.toString().match(/^\s*\[object\s*(\w+)Constructor\]/); + + return matches ? matches[1] : ''; + }; + + j$.isPending_ = function(promise) { + const sentinel = {}; + return Promise.race([promise, Promise.resolve(sentinel)]).then( + function(result) { + return result === sentinel; + }, + function() { + return false; + } + ); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is an instance of the specified class/constructor. + * @name jasmine.any + * @since 1.3.0 + * @function + * @param {Constructor} clazz - The constructor to check against. + */ + j$.any = function(clazz) { + return new j$.Any(clazz); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is not `null` and not `undefined`. + * @name jasmine.anything + * @since 2.2.0 + * @function + */ + j$.anything = function() { + return new j$.Anything(); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is `true` or anything truthy. + * @name jasmine.truthy + * @since 3.1.0 + * @function + */ + j$.truthy = function() { + return new j$.Truthy(); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is `null`, `undefined`, `0`, `false` or anything falsey. + * @name jasmine.falsy + * @since 3.1.0 + * @function + */ + j$.falsy = function() { + return new j$.Falsy(); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is empty. + * @name jasmine.empty + * @since 3.1.0 + * @function + */ + j$.empty = function() { + return new j$.Empty(); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} + * that passes if the actual value is the same as the sample as determined + * by the `===` operator. + * @name jasmine.is + * @function + * @param {Object} sample - The value to compare the actual to. + */ + j$.is = function(sample) { + return new j$.Is(sample); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared is not empty. + * @name jasmine.notEmpty + * @since 3.1.0 + * @function + */ + j$.notEmpty = function() { + return new j$.NotEmpty(); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value being compared contains at least the keys and values. + * @name jasmine.objectContaining + * @since 1.3.0 + * @function + * @param {Object} sample - The subset of properties that _must_ be in the actual. + */ + j$.objectContaining = function(sample) { + return new j$.ObjectContaining(sample); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value is a `String` that matches the `RegExp` or `String`. + * @name jasmine.stringMatching + * @since 2.2.0 + * @function + * @param {RegExp|String} expected + */ + j$.stringMatching = function(expected) { + return new j$.StringMatching(expected); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value is a `String` that contains the specified `String`. + * @name jasmine.stringContaining + * @since 3.10.0 + * @function + * @param {String} expected + */ + j$.stringContaining = function(expected) { + return new j$.StringContaining(expected); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value is an `Array` that contains at least the elements in the sample. + * @name jasmine.arrayContaining + * @since 2.2.0 + * @function + * @param {Array} sample + */ + j$.arrayContaining = function(sample) { + return new j$.ArrayContaining(sample); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if the actual value is an `Array` that contains all of the elements in the sample in any order. + * @name jasmine.arrayWithExactContents + * @since 2.8.0 + * @function + * @param {Array} sample + */ + j$.arrayWithExactContents = function(sample) { + return new j$.ArrayWithExactContents(sample); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if every key/value pair in the sample passes the deep equality comparison + * with at least one key/value pair in the actual value being compared + * @name jasmine.mapContaining + * @since 3.5.0 + * @function + * @param {Map} sample - The subset of items that _must_ be in the actual. + */ + j$.mapContaining = function(sample) { + return new j$.MapContaining(sample); + }; + + /** + * Get an {@link AsymmetricEqualityTester}, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}), + * that will succeed if every item in the sample passes the deep equality comparison + * with at least one item in the actual value being compared + * @name jasmine.setContaining + * @since 3.5.0 + * @function + * @param {Set} sample - The subset of items that _must_ be in the actual. + */ + j$.setContaining = function(sample) { + return new j$.SetContaining(sample); + }; + + /** + * Determines whether the provided function is a Jasmine spy. + * @name jasmine.isSpy + * @since 2.0.0 + * @function + * @param {Function} putativeSpy - The function to check. + * @return {Boolean} + */ + j$.isSpy = function(putativeSpy) { + if (!putativeSpy) { + return false; + } + return ( + putativeSpy.and instanceof j$.SpyStrategy && + putativeSpy.calls instanceof j$.CallTracker + ); + }; + + /** + * Logs a message for use in debugging. If the spec fails, trace messages + * will be included in the {@link SpecResult|result} passed to the + * reporter's specDone method. + * + * This method should be called only when a spec (including any associated + * beforeEach or afterEach functions) is running. + * @function + * @name jasmine.debugLog + * @since 4.0.0 + * @param {String} msg - The message to log + */ + j$.debugLog = function(msg) { + j$.getEnv().debugLog(msg); + }; + + /** + * Replaces Jasmine's global error handling with a spy. This prevents Jasmine + * from treating uncaught exceptions and unhandled promise rejections + * as spec failures and allows them to be inspected using the spy's + * {@link Spy#calls|calls property} and related matchers such as + * {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}. + * + * After installing the spy, spyOnGlobalErrorsAsync immediately calls its + * argument, which must be an async or promise-returning function. The spy + * will be passed as the first argument to that callback. Normal error + * handling will be restored when the promise returned from the callback is + * settled. + * + * Note: The JavaScript runtime may deliver uncaught error events and unhandled + * rejection events asynchronously, especially in browsers. If the event + * occurs after the promise returned from the callback is settled, it won't + * be routed to the spy even if the underlying error occurred previously. + * It's up to you to ensure that the returned promise isn't resolved until + * all of the error/rejection events that you want to handle have occurred. + * + * You must await the return value of spyOnGlobalErrorsAsync. + * @name jasmine.spyOnGlobalErrorsAsync + * @function + * @async + * @param {AsyncFunction} fn - A function to run, during which the global error spy will be effective + * @example + * it('demonstrates global error spies', async function() { + * await jasmine.spyOnGlobalErrorsAsync(async function(globalErrorSpy) { + * setTimeout(function() { + * throw new Error('the expected error'); + * }); + * await new Promise(function(resolve) { + * setTimeout(resolve); + * }); + * const expected = new Error('the expected error'); + * expect(globalErrorSpy).toHaveBeenCalledWith(expected); + * }); + * }); + */ + j$.spyOnGlobalErrorsAsync = async function(fn) { + await jasmine.getEnv().spyOnGlobalErrorsAsync(fn); + }; +}; + +getJasmineRequireObj().util = function(j$) { + const util = {}; + + util.isUndefined = function(obj) { + return obj === void 0; + }; + + util.clone = function(obj) { + if (Object.prototype.toString.apply(obj) === '[object Array]') { + return obj.slice(); + } + + const cloned = {}; + for (const prop in obj) { + if (obj.hasOwnProperty(prop)) { + cloned[prop] = obj[prop]; + } + } + + return cloned; + }; + + util.cloneArgs = function(args) { + return Array.from(args).map(function(arg) { + const str = Object.prototype.toString.apply(arg), + primitives = /^\[object (Boolean|String|RegExp|Number)/; + + // All falsey values are either primitives, `null`, or `undefined. + if (!arg || str.match(primitives)) { + return arg; + } else if (str === '[object Date]') { + return new Date(arg.valueOf()); + } else { + return j$.util.clone(arg); + } + }); + }; + + util.getPropertyDescriptor = function(obj, methodName) { + let descriptor, + proto = obj; + + do { + descriptor = Object.getOwnPropertyDescriptor(proto, methodName); + proto = Object.getPrototypeOf(proto); + } while (!descriptor && proto); + + return descriptor; + }; + + util.has = function(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); + }; + + util.errorWithStack = function errorWithStack() { + // Don't throw and catch. That makes it harder for users to debug their + // code with exception breakpoints, and it's unnecessary since all + // supported environments populate new Error().stack + return new Error(); + }; + + function callerFile() { + const trace = new j$.StackTrace(util.errorWithStack()); + return trace.frames[2].file; + } + + util.jasmineFile = (function() { + let result; + + return function() { + if (!result) { + result = callerFile(); + } + + return result; + }; + })(); + + util.validateTimeout = function(timeout, msgPrefix) { + // Timeouts are implemented with setTimeout, which only supports a limited + // range of values. The limit is unspecified, as is the behavior when it's + // exceeded. But on all currently supported JS runtimes, setTimeout calls + // the callback immediately when the timeout is greater than 2147483647 + // (the maximum value of a signed 32 bit integer). + const max = 2147483647; + + if (timeout > max) { + throw new Error( + (msgPrefix || 'Timeout value') + ' cannot be greater than ' + max + ); + } + }; + + return util; +}; + +getJasmineRequireObj().Spec = function(j$) { + function Spec(attrs) { + this.expectationFactory = attrs.expectationFactory; + this.asyncExpectationFactory = attrs.asyncExpectationFactory; + this.resultCallback = attrs.resultCallback || function() {}; + this.id = attrs.id; + this.filename = attrs.filename; + this.parentSuiteId = attrs.parentSuiteId; + this.description = attrs.description || ''; + this.queueableFn = attrs.queueableFn; + this.beforeAndAfterFns = + attrs.beforeAndAfterFns || + function() { + return { befores: [], afters: [] }; + }; + this.userContext = + attrs.userContext || + function() { + return {}; + }; + this.onStart = attrs.onStart || function() {}; + this.autoCleanClosures = + attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures; + this.getSpecName = + attrs.getSpecName || + function() { + return ''; + }; + this.onLateError = attrs.onLateError || function() {}; + this.catchingExceptions = + attrs.catchingExceptions || + function() { + return true; + }; + this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; + this.timer = attrs.timer || new j$.Timer(); + + if (!this.queueableFn.fn) { + this.exclude(); + } + + this.reset(); + } + + Spec.prototype.addExpectationResult = function(passed, data, isError) { + const expectationResult = j$.buildExpectationResult(data); + + if (passed) { + this.result.passedExpectations.push(expectationResult); + } else { + if (this.reportedDone) { + this.onLateError(expectationResult); + } else { + this.result.failedExpectations.push(expectationResult); + + // TODO: refactor so that we don't need to override cached status + if (this.result.status) { + this.result.status = 'failed'; + } + } + + if (this.throwOnExpectationFailure && !isError) { + throw new j$.errors.ExpectationFailed(); + } + } + }; + + Spec.prototype.setSpecProperty = function(key, value) { + this.result.properties = this.result.properties || {}; + this.result.properties[key] = value; + }; + + Spec.prototype.execute = function( + queueRunnerFactory, + onComplete, + excluded, + failSpecWithNoExp + ) { + const onStart = { + fn: done => { + this.timer.start(); + this.onStart(this, done); + } + }; + + const complete = { + fn: done => { + if (this.autoCleanClosures) { + this.queueableFn.fn = null; + } + this.result.status = this.status(excluded, failSpecWithNoExp); + this.result.duration = this.timer.elapsed(); + + if (this.result.status !== 'failed') { + this.result.debugLogs = null; + } + + this.resultCallback(this.result, done); + }, + type: 'specCleanup' + }; + + const fns = this.beforeAndAfterFns(); + + const runnerConfig = { + isLeaf: true, + queueableFns: [...fns.befores, this.queueableFn, ...fns.afters], + onException: e => this.handleException(e), + onMultipleDone: () => { + // Issue a deprecation. Include the context ourselves and pass + // ignoreRunnable: true, since getting here always means that we've already + // moved on and the current runnable isn't the one that caused the problem. + this.onLateError( + new Error( + 'An asynchronous spec, beforeEach, or afterEach function called its ' + + "'done' callback more than once.\n(in spec: " + + this.getFullName() + + ')' + ) + ); + }, + onComplete: () => { + if (this.result.status === 'failed') { + onComplete(new j$.StopExecutionError('spec failed')); + } else { + onComplete(); + } + }, + userContext: this.userContext(), + runnableName: this.getFullName.bind(this) + }; + + if (this.markedPending || excluded === true) { + runnerConfig.queueableFns = []; + } + + runnerConfig.queueableFns.unshift(onStart); + runnerConfig.queueableFns.push(complete); + + queueRunnerFactory(runnerConfig); + }; + + Spec.prototype.reset = function() { + /** + * @typedef SpecResult + * @property {String} id - The unique id of this spec. + * @property {String} description - The description passed to the {@link it} that created this spec. + * @property {String} fullName - The full description including all ancestors of this spec. + * @property {String|null} parentSuiteId - The ID of the suite containing this spec, or null if this spec is not in a describe(). + * @property {String} filename - The name of the file the spec was defined in. + * @property {Expectation[]} failedExpectations - The list of expectations that failed during execution of this spec. + * @property {Expectation[]} passedExpectations - The list of expectations that passed during execution of this spec. + * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred during execution this spec. + * @property {String} pendingReason - If the spec is {@link pending}, this will be the reason. + * @property {String} status - Once the spec has completed, this string represents the pass/fail status of this spec. + * @property {number} duration - The time in ms used by the spec execution, including any before/afterEach. + * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSpecProperty} + * @property {DebugLogEntry[]|null} debugLogs - Messages, if any, that were logged using {@link jasmine.debugLog} during a failing spec. + * @since 2.0.0 + */ + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + parentSuiteId: this.parentSuiteId, + filename: this.filename, + failedExpectations: [], + passedExpectations: [], + deprecationWarnings: [], + pendingReason: this.excludeMessage || '', + duration: null, + properties: null, + debugLogs: null + }; + this.markedPending = this.markedExcluding; + this.reportedDone = false; + }; + + Spec.prototype.handleException = function handleException(e) { + if (Spec.isPendingSpecException(e)) { + this.pend(extractCustomPendingMessage(e)); + return; + } + + if (e instanceof j$.errors.ExpectationFailed) { + return; + } + + this.addExpectationResult( + false, + { + matcherName: '', + passed: false, + expected: '', + actual: '', + error: e + }, + true + ); + }; + + /* + * Marks state as pending + * @param {string} [message] An optional reason message + */ + Spec.prototype.pend = function(message) { + this.markedPending = true; + if (message) { + this.result.pendingReason = message; + } + }; + + /* + * Like {@link Spec#pend}, but pending state will survive {@link Spec#reset} + * Useful for fit, xit, where pending state remains. + * @param {string} [message] An optional reason message + */ + Spec.prototype.exclude = function(message) { + this.markedExcluding = true; + if (this.message) { + this.excludeMessage = message; + } + this.pend(message); + }; + + Spec.prototype.getResult = function() { + this.result.status = this.status(); + return this.result; + }; + + Spec.prototype.status = function(excluded, failSpecWithNoExpectations) { + if (excluded === true) { + return 'excluded'; + } + + if (this.markedPending) { + return 'pending'; + } + + if ( + this.result.failedExpectations.length > 0 || + (failSpecWithNoExpectations && + this.result.failedExpectations.length + + this.result.passedExpectations.length === + 0) + ) { + return 'failed'; + } + + return 'passed'; + }; + + Spec.prototype.getFullName = function() { + return this.getSpecName(this); + }; + + Spec.prototype.addDeprecationWarning = function(deprecation) { + if (typeof deprecation === 'string') { + deprecation = { message: deprecation }; + } + this.result.deprecationWarnings.push( + j$.buildExpectationResult(deprecation) + ); + }; + + Spec.prototype.debugLog = function(msg) { + if (!this.result.debugLogs) { + this.result.debugLogs = []; + } + + /** + * @typedef DebugLogEntry + * @property {String} message - The message that was passed to {@link jasmine.debugLog}. + * @property {number} timestamp - The time when the entry was added, in + * milliseconds from the spec's start time + */ + this.result.debugLogs.push({ + message: msg, + timestamp: this.timer.elapsed() + }); + }; + + const extractCustomPendingMessage = function(e) { + const fullMessage = e.toString(), + boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), + boilerplateEnd = + boilerplateStart + Spec.pendingSpecExceptionMessage.length; + + return fullMessage.slice(boilerplateEnd); + }; + + Spec.pendingSpecExceptionMessage = '=> marked Pending'; + + Spec.isPendingSpecException = function(e) { + return !!( + e && + e.toString && + e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1 + ); + }; + + /** + * @interface Spec + * @see Configuration#specFilter + * @since 2.0.0 + */ + Object.defineProperty(Spec.prototype, 'metadata', { + get: function() { + if (!this.metadata_) { + this.metadata_ = { + /** + * The unique ID of this spec. + * @name Spec#id + * @readonly + * @type {string} + * @since 2.0.0 + */ + id: this.id, + + /** + * The description passed to the {@link it} that created this spec. + * @name Spec#description + * @readonly + * @type {string} + * @since 2.0.0 + */ + description: this.description, + + /** + * The full description including all ancestors of this spec. + * @name Spec#getFullName + * @function + * @returns {string} + * @since 2.0.0 + */ + getFullName: this.getFullName.bind(this) + }; + } + + return this.metadata_; + } + }); + + return Spec; +}; + +getJasmineRequireObj().Order = function() { + function Order(options) { + this.random = 'random' in options ? options.random : true; + const seed = (this.seed = options.seed || generateSeed()); + this.sort = this.random ? randomOrder : naturalOrder; + + function naturalOrder(items) { + return items; + } + + function randomOrder(items) { + const copy = items.slice(); + copy.sort(function(a, b) { + return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id); + }); + return copy; + } + + function generateSeed() { + return String(Math.random()).slice(-5); + } + + // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function + // used to get a different output when the key changes slightly. + // We use your return to sort the children randomly in a consistent way when + // used in conjunction with a seed + + function jenkinsHash(key) { + let hash, i; + for (hash = i = 0; i < key.length; ++i) { + hash += key.charCodeAt(i); + hash += hash << 10; + hash ^= hash >> 6; + } + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + return hash; + } + } + + return Order; +}; + +getJasmineRequireObj().Env = function(j$) { + /** + * @class Env + * @since 2.0.0 + * @classdesc The Jasmine environment.
+ * _Note:_ Do not construct this directly. You can obtain the Env instance by + * calling {@link jasmine.getEnv}. + * @hideconstructor + */ + function Env(options) { + options = options || {}; + + const self = this; + const global = options.global || j$.getGlobal(); + + const realSetTimeout = global.setTimeout; + const realClearTimeout = global.clearTimeout; + const clearStack = j$.getClearStack(global); + this.clock = new j$.Clock( + global, + function() { + return new j$.DelayedFunctionScheduler(); + }, + new j$.MockDate(global) + ); + + const globalErrors = new j$.GlobalErrors(); + const installGlobalErrors = (function() { + let installed = false; + return function() { + if (!installed) { + globalErrors.install(); + installed = true; + } + }; + })(); + + const runableResources = new j$.RunableResources({ + getCurrentRunableId: function() { + const r = runner.currentRunable(); + return r ? r.id : null; + }, + globalErrors + }); + + let reporter; + let topSuite; + let runner; + let parallelLoadingState = null; // 'specs', 'helpers', or null for non-parallel + + /** + * This represents the available options to configure Jasmine. + * Options that are not provided will use their default values. + * @see Env#configure + * @interface Configuration + * @since 3.3.0 + */ + const config = { + /** + * Whether to randomize spec execution order + * @name Configuration#random + * @since 3.3.0 + * @type Boolean + * @default true + */ + random: true, + /** + * Seed to use as the basis of randomization. + * Null causes the seed to be determined randomly at the start of execution. + * @name Configuration#seed + * @since 3.3.0 + * @type (number|string) + * @default null + */ + seed: null, + /** + * Whether to stop execution of the suite after the first spec failure + * + *

In parallel mode, `stopOnSpecFailure` works on a "best effort" + * basis. Jasmine will stop execution as soon as practical after a failure + * but it might not be immediate.

+ * @name Configuration#stopOnSpecFailure + * @since 3.9.0 + * @type Boolean + * @default false + */ + stopOnSpecFailure: false, + /** + * Whether to fail the spec if it ran no expectations. By default + * a spec that ran no expectations is reported as passed. Setting this + * to true will report such spec as a failure. + * @name Configuration#failSpecWithNoExpectations + * @since 3.5.0 + * @type Boolean + * @default false + */ + failSpecWithNoExpectations: false, + /** + * Whether to cause specs to only have one expectation failure. + * @name Configuration#stopSpecOnExpectationFailure + * @since 3.3.0 + * @type Boolean + * @default false + */ + stopSpecOnExpectationFailure: false, + /** + * A function that takes a spec and returns true if it should be executed + * or false if it should be skipped. + * @callback SpecFilter + * @param {Spec} spec - The spec that the filter is being applied to. + * @return boolean + */ + /** + * Function to use to filter specs + * @name Configuration#specFilter + * @since 3.3.0 + * @type SpecFilter + * @default A function that always returns true. + */ + specFilter: function() { + return true; + }, + /** + * Whether or not reporters should hide disabled specs from their output. + * Currently only supported by Jasmine's HTMLReporter + * @name Configuration#hideDisabled + * @since 3.3.0 + * @type Boolean + * @default false + */ + hideDisabled: false, + /** + * Clean closures when a suite is done running (done by clearing the stored function reference). + * This prevents memory leaks, but you won't be able to run jasmine multiple times. + * @name Configuration#autoCleanClosures + * @since 3.10.0 + * @type boolean + * @default true + */ + autoCleanClosures: true, + /** + * Whether or not to issue warnings for certain deprecated functionality + * every time it's used. If not set or set to false, deprecation warnings + * for methods that tend to be called frequently will be issued only once + * or otherwise throttled to to prevent the suite output from being flooded + * with warnings. + * @name Configuration#verboseDeprecations + * @since 3.6.0 + * @type Boolean + * @default false + */ + verboseDeprecations: false + }; + + if (!options.suppressLoadErrors) { + installGlobalErrors(); + globalErrors.pushListener(function loadtimeErrorHandler(error, event) { + topSuite.result.failedExpectations.push({ + passed: false, + globalErrorType: 'load', + message: error ? error.message : event.message, + stack: error && error.stack, + filename: event && event.filename, + lineno: event && event.lineno + }); + }); + } + + /** + * Configure your jasmine environment + * @name Env#configure + * @since 3.3.0 + * @argument {Configuration} configuration + * @function + */ + this.configure = function(configuration) { + if (parallelLoadingState) { + throw new Error( + 'Jasmine cannot be configured via Env in parallel mode' + ); + } + + const booleanProps = [ + 'random', + 'failSpecWithNoExpectations', + 'hideDisabled', + 'stopOnSpecFailure', + 'stopSpecOnExpectationFailure', + 'autoCleanClosures' + ]; + + booleanProps.forEach(function(prop) { + if (typeof configuration[prop] !== 'undefined') { + config[prop] = !!configuration[prop]; + } + }); + + if (configuration.specFilter) { + config.specFilter = configuration.specFilter; + } + + if (typeof configuration.seed !== 'undefined') { + config.seed = configuration.seed; + } + + if (configuration.hasOwnProperty('verboseDeprecations')) { + config.verboseDeprecations = configuration.verboseDeprecations; + deprecator.verboseDeprecations(config.verboseDeprecations); + } + }; + + /** + * Get the current configuration for your jasmine environment + * @name Env#configuration + * @since 3.3.0 + * @function + * @returns {Configuration} + */ + this.configuration = function() { + const result = {}; + for (const property in config) { + result[property] = config[property]; + } + return result; + }; + + this.setDefaultSpyStrategy = function(defaultStrategyFn) { + runableResources.setDefaultSpyStrategy(defaultStrategyFn); + }; + + this.addSpyStrategy = function(name, fn) { + runableResources.customSpyStrategies()[name] = fn; + }; + + this.addCustomEqualityTester = function(tester) { + runableResources.customEqualityTesters().push(tester); + }; + + this.addMatchers = function(matchersToAdd) { + runableResources.addCustomMatchers(matchersToAdd); + }; + + this.addAsyncMatchers = function(matchersToAdd) { + runableResources.addCustomAsyncMatchers(matchersToAdd); + }; + + this.addCustomObjectFormatter = function(formatter) { + runableResources.customObjectFormatters().push(formatter); + }; + + j$.Expectation.addCoreMatchers(j$.matchers); + j$.Expectation.addAsyncCoreMatchers(j$.asyncMatchers); + + const expectationFactory = function(actual, spec) { + return j$.Expectation.factory({ + matchersUtil: runableResources.makeMatchersUtil(), + customMatchers: runableResources.customMatchers(), + actual: actual, + addExpectationResult: addExpectationResult + }); + + function addExpectationResult(passed, result) { + return spec.addExpectationResult(passed, result); + } + }; + + const handleThrowUnlessFailure = function(passed, result) { + if (!passed) { + /** + * @interface + * @name ThrowUnlessFailure + * @extends Error + * @description Represents a failure of an expectation evaluated with + * {@link throwUnless}. Properties of this error are a subset of the + * properties of {@link Expectation} and have the same values. + * @property {String} matcherName - The name of the matcher that was executed for this expectation. + * @property {String} message - The failure message for the expectation. + * @property {Boolean} passed - Whether the expectation passed or failed. + * @property {Object} expected - If the expectation failed, what was the expected value. + * @property {Object} actual - If the expectation failed, what actual value was produced. + */ + const error = new Error(result.message); + error.passed = result.passed; + error.message = result.message; + error.expected = result.expected; + error.actual = result.actual; + error.matcherName = result.matcherName; + throw error; + } + }; + + const throwUnlessFactory = function(actual, spec) { + return j$.Expectation.factory({ + matchersUtil: runableResources.makeMatchersUtil(), + customMatchers: runableResources.customMatchers(), + actual: actual, + addExpectationResult: handleThrowUnlessFailure + }); + }; + + const throwUnlessAsyncFactory = function(actual, spec) { + return j$.Expectation.asyncFactory({ + matchersUtil: runableResources.makeMatchersUtil(), + customAsyncMatchers: runableResources.customAsyncMatchers(), + actual: actual, + addExpectationResult: handleThrowUnlessFailure + }); + }; + + // TODO: Unify recordLateError with recordLateExpectation? The extra + // diagnostic info added by the latter is probably useful in most cases. + function recordLateError(error) { + const isExpectationResult = + error.matcherName !== undefined && error.passed !== undefined; + const result = isExpectationResult + ? error + : j$.buildExpectationResult({ + error, + passed: false, + matcherName: '', + expected: '', + actual: '' + }); + routeLateFailure(result); + } + + function recordLateExpectation(runable, runableType, result) { + const delayedExpectationResult = {}; + Object.keys(result).forEach(function(k) { + delayedExpectationResult[k] = result[k]; + }); + delayedExpectationResult.passed = false; + delayedExpectationResult.globalErrorType = 'lateExpectation'; + delayedExpectationResult.message = + runableType + + ' "' + + runable.getFullName() + + '" ran a "' + + result.matcherName + + '" expectation after it finished.\n'; + + if (result.message) { + delayedExpectationResult.message += + 'Message: "' + result.message + '"\n'; + } + + delayedExpectationResult.message += + '1. Did you forget to return or await the result of expectAsync?\n' + + '2. Was done() invoked before an async operation completed?\n' + + '3. Did an expectation follow a call to done()?'; + + topSuite.result.failedExpectations.push(delayedExpectationResult); + } + + function routeLateFailure(expectationResult) { + // Report the result on the nearest ancestor suite that hasn't already + // been reported done. + for (let r = runner.currentRunable(); r; r = r.parentSuite) { + if (!r.reportedDone) { + if (r === topSuite) { + expectationResult.globalErrorType = 'lateError'; + } + + r.result.failedExpectations.push(expectationResult); + return; + } + } + + // If we get here, all results have been reported and there's nothing we + // can do except log the result and hope the user sees it. + console.error('Jasmine received a result after the suite finished:'); + console.error(expectationResult); + } + + const asyncExpectationFactory = function(actual, spec, runableType) { + return j$.Expectation.asyncFactory({ + matchersUtil: runableResources.makeMatchersUtil(), + customAsyncMatchers: runableResources.customAsyncMatchers(), + actual: actual, + addExpectationResult: addExpectationResult + }); + + function addExpectationResult(passed, result) { + if (runner.currentRunable() !== spec) { + recordLateExpectation(spec, runableType, result); + } + return spec.addExpectationResult(passed, result); + } + }; + + /** + * Causes a deprecation warning to be logged to the console and reported to + * reporters. + * + * The optional second parameter is an object that can have either of the + * following properties: + * + * omitStackTrace: Whether to omit the stack trace. Optional. Defaults to + * false. This option is ignored if the deprecation is an Error. Set this + * when the stack trace will not contain anything that helps the user find + * the source of the deprecation. + * + * ignoreRunnable: Whether to log the deprecation on the root suite, ignoring + * the spec or suite that's running when it happens. Optional. Defaults to + * false. + * + * @name Env#deprecated + * @since 2.99 + * @function + * @param {String|Error} deprecation The deprecation message + * @param {Object} [options] Optional extra options, as described above + */ + this.deprecated = function(deprecation, options) { + const runable = runner.currentRunable() || topSuite; + deprecator.addDeprecationWarning(runable, deprecation, options); + }; + + function queueRunnerFactory(options) { + options.clearStack = options.clearStack || clearStack; + options.timeout = { + setTimeout: realSetTimeout, + clearTimeout: realClearTimeout + }; + options.fail = self.fail; + options.globalErrors = globalErrors; + options.onException = + options.onException || + function(e) { + (runner.currentRunable() || topSuite).handleException(e); + }; + + new j$.QueueRunner(options).execute(); + } + + const suiteBuilder = new j$.SuiteBuilder({ + env: this, + expectationFactory, + asyncExpectationFactory, + onLateError: recordLateError, + specResultCallback, + specStarted, + queueRunnerFactory + }); + topSuite = suiteBuilder.topSuite; + const deprecator = new j$.Deprecator(topSuite); + + /** + * Provides the root suite, through which all suites and specs can be + * accessed. + * @function + * @name Env#topSuite + * @return {Suite} the root suite + * @since 2.0.0 + */ + this.topSuite = function() { + ensureNonParallel('topSuite'); + return topSuite.metadata; + }; + + /** + * This represents the available reporter callback for an object passed to {@link Env#addReporter}. + * @interface Reporter + * @see custom_reporter + */ + reporter = new j$.ReportDispatcher( + j$.reporterEvents, + function(options) { + options.SkipPolicy = j$.NeverSkipPolicy; + return queueRunnerFactory(options); + }, + recordLateError + ); + + runner = new j$.Runner({ + topSuite, + totalSpecsDefined: () => suiteBuilder.totalSpecsDefined, + focusedRunables: () => suiteBuilder.focusedRunables, + runableResources, + reporter, + queueRunnerFactory, + getConfig: () => config, + reportSpecDone + }); + + this.setParallelLoadingState = function(state) { + parallelLoadingState = state; + }; + + this.parallelReset = function() { + suiteBuilder.parallelReset(); + runner.parallelReset(); + }; + + /** + * Executes the specs. + * + * If called with no parameter or with a falsy parameter, + * all specs will be executed except those that are excluded by a + * [spec filter]{@link Configuration#specFilter} or other mechanism. If the + * parameter is a list of spec/suite IDs, only those specs/suites will + * be run. + * + * execute should not be called more than once unless the env has been + * configured with `{autoCleanClosures: false}`. + * + * execute returns a promise. The promise will be resolved to the same + * {@link JasmineDoneInfo|overall result} that's passed to a reporter's + * `jasmineDone` method, even if the suite did not pass. To determine + * whether the suite passed, check the value that the promise resolves to + * or use a {@link Reporter}. The promise will be rejected in the case of + * certain serious errors that prevent execution from starting. + * + * @name Env#execute + * @since 2.0.0 + * @function + * @async + * @param {(string[])=} runablesToRun IDs of suites and/or specs to run + * @return {Promise} + */ + this.execute = async function(runablesToRun) { + installGlobalErrors(); + + if (parallelLoadingState) { + validateConfigForParallel(); + } + + const result = await runner.execute(runablesToRun); + this.cleanup_(); + return result; + }; + + /** + * Add a custom reporter to the Jasmine environment. + * @name Env#addReporter + * @since 2.0.0 + * @function + * @param {Reporter} reporterToAdd The reporter to be added. + * @see custom_reporter + */ + this.addReporter = function(reporterToAdd) { + if (parallelLoadingState) { + throw new Error('Reporters cannot be added via Env in parallel mode'); + } + + reporter.addReporter(reporterToAdd); + }; + + /** + * Provide a fallback reporter if no other reporters have been specified. + * @name Env#provideFallbackReporter + * @since 2.5.0 + * @function + * @param {Reporter} reporterToAdd The reporter + * @see custom_reporter + */ + this.provideFallbackReporter = function(reporterToAdd) { + reporter.provideFallbackReporter(reporterToAdd); + }; + + /** + * Clear all registered reporters + * @name Env#clearReporters + * @since 2.5.2 + * @function + */ + this.clearReporters = function() { + if (parallelLoadingState) { + throw new Error('Reporters cannot be removed via Env in parallel mode'); + } + + reporter.clearReporters(); + }; + + /** + * Configures whether Jasmine should allow the same function to be spied on + * more than once during the execution of a spec. By default, spying on + * a function that is already a spy will cause an error. + * @name Env#allowRespy + * @function + * @since 2.5.0 + * @param {boolean} allow Whether to allow respying + */ + this.allowRespy = function(allow) { + runableResources.spyRegistry.allowRespy(allow); + }; + + this.spyOn = function() { + return runableResources.spyRegistry.spyOn.apply( + runableResources.spyRegistry, + arguments + ); + }; + + this.spyOnProperty = function() { + return runableResources.spyRegistry.spyOnProperty.apply( + runableResources.spyRegistry, + arguments + ); + }; + + this.spyOnAllFunctions = function() { + return runableResources.spyRegistry.spyOnAllFunctions.apply( + runableResources.spyRegistry, + arguments + ); + }; + + this.createSpy = function(name, originalFn) { + return runableResources.spyFactory.createSpy(name, originalFn); + }; + + this.createSpyObj = function(baseName, methodNames, propertyNames) { + return runableResources.spyFactory.createSpyObj( + baseName, + methodNames, + propertyNames + ); + }; + + this.spyOnGlobalErrorsAsync = async function(fn) { + const spy = this.createSpy('global error handler'); + const associatedRunable = runner.currentRunable(); + let cleanedUp = false; + + globalErrors.setOverrideListener(spy, () => { + if (!cleanedUp) { + const message = + 'Global error spy was not uninstalled. (Did you ' + + 'forget to await the return value of spyOnGlobalErrorsAsync?)'; + associatedRunable.addExpectationResult(false, { + matcherName: '', + passed: false, + expected: '', + actual: '', + message, + error: null + }); + } + + cleanedUp = true; + }); + + try { + const maybePromise = fn(spy); + + if (!j$.isPromiseLike(maybePromise)) { + throw new Error( + 'The callback to spyOnGlobalErrorsAsync must be an async or promise-returning function' + ); + } + + await maybePromise; + } finally { + if (!cleanedUp) { + cleanedUp = true; + globalErrors.removeOverrideListener(); + } + } + }; + + function ensureIsNotNested(method) { + const runable = runner.currentRunable(); + if (runable !== null && runable !== undefined) { + throw new Error( + "'" + method + "' should only be used in 'describe' function" + ); + } + } + + function ensureNonParallel(method) { + if (parallelLoadingState) { + throw new Error(`'${method}' is not available in parallel mode`); + } + } + + function ensureNonParallelOrInDescribe(msg) { + if (parallelLoadingState && !suiteBuilder.inDescribe()) { + throw new Error(msg); + } + } + + function ensureNonParallelOrInHelperOrInDescribe(method) { + if (parallelLoadingState === 'specs' && !suiteBuilder.inDescribe()) { + throw new Error( + 'In parallel mode, ' + + method + + ' must be in a describe block or in a helper file' + ); + } + } + + function validateConfigForParallel() { + if (!config.random) { + throw new Error('Randomization cannot be disabled in parallel mode'); + } + + if (config.seed !== null && config.seed !== undefined) { + throw new Error('Random seed cannot be set in parallel mode'); + } + } + + this.describe = function(description, definitionFn) { + ensureIsNotNested('describe'); + const filename = callerCallerFilename(); + return suiteBuilder.describe(description, definitionFn, filename) + .metadata; + }; + + this.xdescribe = function(description, definitionFn) { + ensureIsNotNested('xdescribe'); + const filename = callerCallerFilename(); + return suiteBuilder.xdescribe(description, definitionFn, filename) + .metadata; + }; + + this.fdescribe = function(description, definitionFn) { + ensureIsNotNested('fdescribe'); + ensureNonParallel('fdescribe'); + const filename = callerCallerFilename(); + return suiteBuilder.fdescribe(description, definitionFn, filename) + .metadata; + }; + + function specResultCallback(spec, result, next) { + runableResources.clearForRunable(spec.id); + runner.currentSpec = null; + + if (result.status === 'failed') { + runner.hasFailures = true; + } + + reportSpecDone(spec, result, next); + } + + function specStarted(spec, suite, next) { + runner.currentSpec = spec; + runableResources.initForRunable(spec.id, suite.id); + reporter.specStarted(spec.result).then(next); + } + + function reportSpecDone(spec, result, next) { + spec.reportedDone = true; + reporter.specDone(result).then(next); + } + + this.it = function(description, fn, timeout) { + ensureIsNotNested('it'); + const filename = callerCallerFilename(); + return suiteBuilder.it(description, fn, timeout, filename).metadata; + }; + + this.xit = function(description, fn, timeout) { + ensureIsNotNested('xit'); + const filename = callerCallerFilename(); + return suiteBuilder.xit(description, fn, timeout, filename).metadata; + }; + + this.fit = function(description, fn, timeout) { + ensureIsNotNested('fit'); + ensureNonParallel('fit'); + const filename = callerCallerFilename(); + return suiteBuilder.fit(description, fn, timeout, filename).metadata; + }; + + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} + * @name Env#setSpecProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + this.setSpecProperty = function(key, value) { + if ( + !runner.currentRunable() || + runner.currentRunable() == runner.currentSuite() + ) { + throw new Error( + "'setSpecProperty' was used when there was no current spec" + ); + } + runner.currentRunable().setSpecProperty(key, value); + }; + + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} + * @name Env#setSuiteProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + this.setSuiteProperty = function(key, value) { + if (!runner.currentSuite()) { + throw new Error( + "'setSuiteProperty' was used when there was no current suite" + ); + } + runner.currentSuite().setSuiteProperty(key, value); + }; + + this.debugLog = function(msg) { + const maybeSpec = runner.currentRunable(); + + if (!maybeSpec || !maybeSpec.debugLog) { + throw new Error("'debugLog' was called when there was no current spec"); + } + + maybeSpec.debugLog(msg); + }; + + this.expect = function(actual) { + const runable = runner.currentRunable(); + + if (!runable) { + throw new Error( + "'expect' was used when there was no current spec, this could be because an asynchronous test timed out" + ); + } + + return runable.expectationFactory(actual, runable); + }; + + this.expectAsync = function(actual) { + const runable = runner.currentRunable(); + + if (!runable) { + throw new Error( + "'expectAsync' was used when there was no current spec, this could be because an asynchronous test timed out" + ); + } + + return runable.asyncExpectationFactory(actual, runable); + }; + + this.throwUnless = function(actual) { + const runable = runner.currentRunable(); + return throwUnlessFactory(actual, runable); + }; + + this.throwUnlessAsync = function(actual) { + const runable = runner.currentRunable(); + return throwUnlessAsyncFactory(actual, runable); + }; + + this.beforeEach = function(beforeEachFunction, timeout) { + ensureIsNotNested('beforeEach'); + ensureNonParallelOrInHelperOrInDescribe('beforeEach'); + suiteBuilder.beforeEach(beforeEachFunction, timeout); + }; + + this.beforeAll = function(beforeAllFunction, timeout) { + ensureIsNotNested('beforeAll'); + // This message is -npm-specific, but currently parallel operation is + // only supported via -npm. + ensureNonParallelOrInDescribe( + "In parallel mode, 'beforeAll' " + + 'must be in a describe block. Use the globalSetup config ' + + 'property for exactly-once setup in parallel mode.' + ); + suiteBuilder.beforeAll(beforeAllFunction, timeout); + }; + + this.afterEach = function(afterEachFunction, timeout) { + ensureIsNotNested('afterEach'); + ensureNonParallelOrInHelperOrInDescribe('afterEach'); + suiteBuilder.afterEach(afterEachFunction, timeout); + }; + + this.afterAll = function(afterAllFunction, timeout) { + ensureIsNotNested('afterAll'); + // This message is -npm-specific, but currently parallel operation is + // only supported via -npm. + ensureNonParallelOrInDescribe( + "In parallel mode, 'afterAll' " + + 'must be in a describe block. Use the globalTeardown config ' + + 'property for exactly-once teardown in parallel mode.' + ); + suiteBuilder.afterAll(afterAllFunction, timeout); + }; + + this.pending = function(message) { + let fullMessage = j$.Spec.pendingSpecExceptionMessage; + if (message) { + fullMessage += message; + } + throw fullMessage; + }; + + this.fail = function(error) { + if (!runner.currentRunable()) { + throw new Error( + "'fail' was used when there was no current spec, this could be because an asynchronous test timed out" + ); + } + + let message = 'Failed'; + if (error) { + message += ': '; + if (error.message) { + message += error.message; + } else if (j$.isString_(error)) { + message += error; + } else { + // pretty print all kind of objects. This includes arrays. + const pp = runableResources.makePrettyPrinter(); + message += pp(error); + } + } + + runner.currentRunable().addExpectationResult(false, { + matcherName: '', + passed: false, + expected: '', + actual: '', + message: message, + error: error && error.message ? error : null + }); + + if (config.stopSpecOnExpectationFailure) { + throw new Error(message); + } + }; + + this.cleanup_ = function() { + if (globalErrors) { + globalErrors.uninstall(); + } + }; + } + + function callerCallerFilename() { + const frames = new j$.StackTrace(new Error()).frames; + // frames[3] should always exist except in Jasmine's own tests, which bypass + // the global it/describe layer, but don't crash if it doesn't. + return frames[3] && frames[3].file; + } + + return Env; +}; + +getJasmineRequireObj().JsApiReporter = function(j$) { + /** + * @name jsApiReporter + * @classdesc {@link Reporter} added by default in `boot.js` to record results for retrieval in javascript code. An instance is made available as `jsApiReporter` on the global object. + * @class + * @hideconstructor + */ + function JsApiReporter(options) { + const timer = options.timer || new j$.Timer(); + let status = 'loaded'; + + this.started = false; + this.finished = false; + this.runDetails = {}; + + this.jasmineStarted = function() { + this.started = true; + status = 'started'; + timer.start(); + }; + + let executionTime; + + this.jasmineDone = function(runDetails) { + this.finished = true; + this.runDetails = runDetails; + executionTime = timer.elapsed(); + status = 'done'; + }; + + /** + * Get the current status for the Jasmine environment. + * @name jsApiReporter#status + * @since 2.0.0 + * @function + * @return {String} - One of `loaded`, `started`, or `done` + */ + this.status = function() { + return status; + }; + + const suites = [], + suites_hash = {}; + + this.suiteStarted = function(result) { + suites_hash[result.id] = result; + }; + + this.suiteDone = function(result) { + storeSuite(result); + }; + + /** + * Get the results for a set of suites. + * + * Retrievable in slices for easier serialization. + * @name jsApiReporter#suiteResults + * @since 2.1.0 + * @function + * @param {Number} index - The position in the suites list to start from. + * @param {Number} length - Maximum number of suite results to return. + * @return {SuiteResult[]} + */ + this.suiteResults = function(index, length) { + return suites.slice(index, index + length); + }; + + function storeSuite(result) { + suites.push(result); + suites_hash[result.id] = result; + } + + /** + * Get all of the suites in a single object, with their `id` as the key. + * @name jsApiReporter#suites + * @since 2.0.0 + * @function + * @return {Object} - Map of suite id to {@link SuiteResult} + */ + this.suites = function() { + return suites_hash; + }; + + const specs = []; + + this.specDone = function(result) { + specs.push(result); + }; + + /** + * Get the results for a set of specs. + * + * Retrievable in slices for easier serialization. + * @name jsApiReporter#specResults + * @since 2.0.0 + * @function + * @param {Number} index - The position in the specs list to start from. + * @param {Number} length - Maximum number of specs results to return. + * @return {SpecResult[]} + */ + this.specResults = function(index, length) { + return specs.slice(index, index + length); + }; + + /** + * Get all spec results. + * @name jsApiReporter#specs + * @since 2.0.0 + * @function + * @return {SpecResult[]} + */ + this.specs = function() { + return specs; + }; + + /** + * Get the number of milliseconds it took for the full Jasmine suite to run. + * @name jsApiReporter#executionTime + * @since 2.0.0 + * @function + * @return {Number} + */ + this.executionTime = function() { + return executionTime; + }; + } + + return JsApiReporter; +}; + +getJasmineRequireObj().Any = function(j$) { + function Any(expectedObject) { + if (typeof expectedObject === 'undefined') { + throw new TypeError( + 'jasmine.any() expects to be passed a constructor function. ' + + 'Please pass one or use jasmine.anything() to match any object.' + ); + } + this.expectedObject = expectedObject; + } + + Any.prototype.asymmetricMatch = function(other) { + if (this.expectedObject == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedObject == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedObject == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedObject == Object) { + return other !== null && typeof other == 'object'; + } + + if (this.expectedObject == Boolean) { + return typeof other == 'boolean'; + } + + if (typeof Symbol != 'undefined' && this.expectedObject == Symbol) { + return typeof other == 'symbol'; + } + + return other instanceof this.expectedObject; + }; + + Any.prototype.jasmineToString = function() { + return ''; + }; + + return Any; +}; + +getJasmineRequireObj().Anything = function(j$) { + function Anything() {} + + Anything.prototype.asymmetricMatch = function(other) { + return !j$.util.isUndefined(other) && other !== null; + }; + + Anything.prototype.jasmineToString = function() { + return ''; + }; + + return Anything; +}; + +getJasmineRequireObj().ArrayContaining = function(j$) { + function ArrayContaining(sample) { + this.sample = sample; + } + + ArrayContaining.prototype.asymmetricMatch = function(other, matchersUtil) { + if (!j$.isArray_(this.sample)) { + throw new Error( + 'You must provide an array to arrayContaining, not ' + + j$.basicPrettyPrinter_(this.sample) + + '.' + ); + } + + // If the actual parameter is not an array, we can fail immediately, since it couldn't + // possibly be an "array containing" anything. However, we also want an empty sample + // array to match anything, so we need to double-check we aren't in that case + if (!j$.isArray_(other) && this.sample.length > 0) { + return false; + } + + for (const item of this.sample) { + if (!matchersUtil.contains(other, item)) { + return false; + } + } + + return true; + }; + + ArrayContaining.prototype.jasmineToString = function(pp) { + return ''; + }; + + return ArrayContaining; +}; + +getJasmineRequireObj().ArrayWithExactContents = function(j$) { + function ArrayWithExactContents(sample) { + this.sample = sample; + } + + ArrayWithExactContents.prototype.asymmetricMatch = function( + other, + matchersUtil + ) { + if (!j$.isArray_(this.sample)) { + throw new Error( + 'You must provide an array to arrayWithExactContents, not ' + + j$.basicPrettyPrinter_(this.sample) + + '.' + ); + } + + if (this.sample.length !== other.length) { + return false; + } + + for (const item of this.sample) { + if (!matchersUtil.contains(other, item)) { + return false; + } + } + + return true; + }; + + ArrayWithExactContents.prototype.jasmineToString = function(pp) { + return ''; + }; + + return ArrayWithExactContents; +}; + +getJasmineRequireObj().Empty = function(j$) { + function Empty() {} + + Empty.prototype.asymmetricMatch = function(other) { + if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { + return other.length === 0; + } + + if (j$.isMap(other) || j$.isSet(other)) { + return other.size === 0; + } + + if (j$.isObject_(other)) { + return Object.keys(other).length === 0; + } + return false; + }; + + Empty.prototype.jasmineToString = function() { + return ''; + }; + + return Empty; +}; + +getJasmineRequireObj().Falsy = function(j$) { + function Falsy() {} + + Falsy.prototype.asymmetricMatch = function(other) { + return !other; + }; + + Falsy.prototype.jasmineToString = function() { + return ''; + }; + + return Falsy; +}; + +getJasmineRequireObj().Is = function(j$) { + class Is { + constructor(expected) { + this.expected_ = expected; + } + + asymmetricMatch(actual) { + return actual === this.expected_; + } + + jasmineToString(pp) { + return ``; + } + } + + return Is; +}; + +getJasmineRequireObj().MapContaining = function(j$) { + function MapContaining(sample) { + if (!j$.isMap(sample)) { + throw new Error( + 'You must provide a map to `mapContaining`, not ' + + j$.basicPrettyPrinter_(sample) + ); + } + + this.sample = sample; + } + + MapContaining.prototype.asymmetricMatch = function(other, matchersUtil) { + if (!j$.isMap(other)) return false; + + for (const [key, value] of this.sample) { + // for each key/value pair in `sample` + // there should be at least one pair in `other` whose key and value both match + let hasMatch = false; + for (const [oKey, oValue] of other) { + if ( + matchersUtil.equals(oKey, key) && + matchersUtil.equals(oValue, value) + ) { + hasMatch = true; + break; + } + } + + if (!hasMatch) { + return false; + } + } + + return true; + }; + + MapContaining.prototype.jasmineToString = function(pp) { + return ''; + }; + + return MapContaining; +}; + +getJasmineRequireObj().NotEmpty = function(j$) { + function NotEmpty() {} + + NotEmpty.prototype.asymmetricMatch = function(other) { + if (j$.isString_(other) || j$.isArray_(other) || j$.isTypedArray_(other)) { + return other.length !== 0; + } + + if (j$.isMap(other) || j$.isSet(other)) { + return other.size !== 0; + } + + if (j$.isObject_(other)) { + return Object.keys(other).length !== 0; + } + + return false; + }; + + NotEmpty.prototype.jasmineToString = function() { + return ''; + }; + + return NotEmpty; +}; + +getJasmineRequireObj().ObjectContaining = function(j$) { + function ObjectContaining(sample) { + this.sample = sample; + } + + function hasProperty(obj, property) { + if (!obj || typeof obj !== 'object') { + return false; + } + + if (Object.prototype.hasOwnProperty.call(obj, property)) { + return true; + } + + return hasProperty(Object.getPrototypeOf(obj), property); + } + + ObjectContaining.prototype.asymmetricMatch = function(other, matchersUtil) { + if (typeof this.sample !== 'object') { + throw new Error( + "You must provide an object to objectContaining, not '" + + this.sample + + "'." + ); + } + if (typeof other !== 'object') { + return false; + } + + for (const property in this.sample) { + if ( + !hasProperty(other, property) || + !matchersUtil.equals(this.sample[property], other[property]) + ) { + return false; + } + } + + return true; + }; + + ObjectContaining.prototype.valuesForDiff_ = function(other, pp) { + if (!j$.isObject_(other)) { + return { + self: this.jasmineToString(pp), + other: other + }; + } + + const filteredOther = {}; + Object.keys(this.sample).forEach(function(k) { + // eq short-circuits comparison of objects that have different key sets, + // so include all keys even if undefined. + filteredOther[k] = other[k]; + }); + + return { + self: this.sample, + other: filteredOther + }; + }; + + ObjectContaining.prototype.jasmineToString = function(pp) { + return ''; + }; + + return ObjectContaining; +}; + +getJasmineRequireObj().SetContaining = function(j$) { + function SetContaining(sample) { + if (!j$.isSet(sample)) { + throw new Error( + 'You must provide a set to `setContaining`, not ' + + j$.basicPrettyPrinter_(sample) + ); + } + + this.sample = sample; + } + + SetContaining.prototype.asymmetricMatch = function(other, matchersUtil) { + if (!j$.isSet(other)) return false; + + for (const item of this.sample) { + // for each item in `sample` there should be at least one matching item in `other` + // (not using `matchersUtil.contains` because it compares set members by reference, + // not by deep value equality) + let hasMatch = false; + for (const oItem of other) { + if (matchersUtil.equals(oItem, item)) { + hasMatch = true; + break; + } + } + + if (!hasMatch) { + return false; + } + } + + return true; + }; + + SetContaining.prototype.jasmineToString = function(pp) { + return ''; + }; + + return SetContaining; +}; + +getJasmineRequireObj().StringContaining = function(j$) { + function StringContaining(expected) { + if (!j$.isString_(expected)) { + throw new Error('Expected is not a String'); + } + + this.expected = expected; + } + + StringContaining.prototype.asymmetricMatch = function(other) { + if (!j$.isString_(other)) { + // Arrays, etc. don't match no matter what their indexOf returns. + return false; + } + + return other.indexOf(this.expected) !== -1; + }; + + StringContaining.prototype.jasmineToString = function() { + return ''; + }; + + return StringContaining; +}; + +getJasmineRequireObj().StringMatching = function(j$) { + function StringMatching(expected) { + if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { + throw new Error('Expected is not a String or a RegExp'); + } + + this.regexp = new RegExp(expected); + } + + StringMatching.prototype.asymmetricMatch = function(other) { + return this.regexp.test(other); + }; + + StringMatching.prototype.jasmineToString = function() { + return ''; + }; + + return StringMatching; +}; + +getJasmineRequireObj().Truthy = function(j$) { + function Truthy() {} + + Truthy.prototype.asymmetricMatch = function(other) { + return !!other; + }; + + Truthy.prototype.jasmineToString = function() { + return ''; + }; + + return Truthy; +}; + +//TODO: expectation result may make more sense as a presentation of an expectation. +getJasmineRequireObj().buildExpectationResult = function(j$) { + function buildExpectationResult(options) { + const exceptionFormatter = new j$.ExceptionFormatter(); + + /** + * @typedef Expectation + * @property {String} matcherName - The name of the matcher that was executed for this expectation. + * @property {String} message - The failure message for the expectation. + * @property {String} stack - The stack trace for the failure if available. + * @property {Boolean} passed - Whether the expectation passed or failed. + * @property {Object} expected - If the expectation failed, what was the expected value. + * @property {Object} actual - If the expectation failed, what actual value was produced. + * @property {String|undefined} globalErrorType - The type of an error that + * is reported on the top suite. Valid values are undefined, "afterAll", + * "load", "lateExpectation", and "lateError". + */ + const result = { + matcherName: options.matcherName, + message: message(), + stack: options.omitStackTrace ? '' : stack(), + passed: options.passed + }; + + if (!result.passed) { + result.expected = options.expected; + result.actual = options.actual; + + if (options.error && !j$.isString_(options.error)) { + if ('code' in options.error) { + result.code = options.error.code; + } + + if ( + options.error.code === 'ERR_ASSERTION' && + options.expected === '' && + options.actual === '' + ) { + result.expected = options.error.expected; + result.actual = options.error.actual; + result.matcherName = 'assert ' + options.error.operator; + } + } + } + + return result; + + function message() { + if (options.passed) { + return 'Passed.'; + } else if (options.message) { + return options.message; + } else if (options.error) { + return exceptionFormatter.message(options.error); + } + return ''; + } + + function stack() { + if (options.passed) { + return ''; + } + + let error = options.error; + + if (!error) { + if (options.errorForStack) { + error = options.errorForStack; + } else if (options.stack) { + error = options; + } else { + error = new Error(message()); + } + } + // Omit the message from the stack trace because it will be + // included elsewhere. + return exceptionFormatter.stack(error, { omitMessage: true }); + } + } + + return buildExpectationResult; +}; + +getJasmineRequireObj().CallTracker = function(j$) { + /** + * @namespace Spy#calls + * @since 2.0.0 + */ + function CallTracker() { + let calls = []; + const opts = {}; + + this.track = function(context) { + if (opts.cloneArgs) { + context.args = j$.util.cloneArgs(context.args); + } + calls.push(context); + }; + + /** + * Check whether this spy has been invoked. + * @name Spy#calls#any + * @since 2.0.0 + * @function + * @return {Boolean} + */ + this.any = function() { + return !!calls.length; + }; + + /** + * Get the number of invocations of this spy. + * @name Spy#calls#count + * @since 2.0.0 + * @function + * @return {Integer} + */ + this.count = function() { + return calls.length; + }; + + /** + * Get the arguments that were passed to a specific invocation of this spy. + * @name Spy#calls#argsFor + * @since 2.0.0 + * @function + * @param {Integer} index The 0-based invocation index. + * @return {Array} + */ + this.argsFor = function(index) { + const call = calls[index]; + return call ? call.args : []; + }; + + /** + * Get the "this" object that was passed to a specific invocation of this spy. + * @name Spy#calls#thisFor + * @since 3.8.0 + * @function + * @param {Integer} index The 0-based invocation index. + * @return {Object?} + */ + this.thisFor = function(index) { + const call = calls[index]; + return call ? call.object : undefined; + }; + + /** + * Get the raw calls array for this spy. + * @name Spy#calls#all + * @since 2.0.0 + * @function + * @return {Spy.callData[]} + */ + this.all = function() { + return calls; + }; + + /** + * Get all of the arguments for each invocation of this spy in the order they were received. + * @name Spy#calls#allArgs + * @since 2.0.0 + * @function + * @return {Array} + */ + this.allArgs = function() { + return calls.map(c => c.args); + }; + + /** + * Get the first invocation of this spy. + * @name Spy#calls#first + * @since 2.0.0 + * @function + * @return {ObjecSpy.callData} + */ + this.first = function() { + return calls[0]; + }; + + /** + * Get the most recent invocation of this spy. + * @name Spy#calls#mostRecent + * @since 2.0.0 + * @function + * @return {ObjecSpy.callData} + */ + this.mostRecent = function() { + return calls[calls.length - 1]; + }; + + /** + * Reset this spy as if it has never been called. + * @name Spy#calls#reset + * @since 2.0.0 + * @function + */ + this.reset = function() { + calls = []; + }; + + /** + * Set this spy to do a shallow clone of arguments passed to each invocation. + * @name Spy#calls#saveArgumentsByValue + * @since 2.5.0 + * @function + */ + this.saveArgumentsByValue = function() { + opts.cloneArgs = true; + }; + } + + return CallTracker; +}; + +getJasmineRequireObj().clearStack = function(j$) { + const maxInlineCallCount = 10; + + function browserQueueMicrotaskImpl(global) { + const { setTimeout, queueMicrotask } = global; + let currentCallCount = 0; + return function clearStack(fn) { + currentCallCount++; + + if (currentCallCount < maxInlineCallCount) { + queueMicrotask(fn); + } else { + currentCallCount = 0; + setTimeout(fn); + } + }; + } + + function nodeQueueMicrotaskImpl(global) { + const { queueMicrotask } = global; + + return function(fn) { + queueMicrotask(fn); + }; + } + + function messageChannelImpl(global) { + const { MessageChannel, setTimeout } = global; + const channel = new MessageChannel(); + let head = {}; + let tail = head; + + let taskRunning = false; + channel.port1.onmessage = function() { + head = head.next; + const task = head.task; + delete head.task; + + if (taskRunning) { + setTimeout(task, 0); + } else { + try { + taskRunning = true; + task(); + } finally { + taskRunning = false; + } + } + }; + + let currentCallCount = 0; + return function clearStack(fn) { + currentCallCount++; + + if (currentCallCount < maxInlineCallCount) { + tail = tail.next = { task: fn }; + channel.port2.postMessage(0); + } else { + currentCallCount = 0; + setTimeout(fn); + } + }; + } + + function getClearStack(global) { + const NODE_JS = + global.process && + global.process.versions && + typeof global.process.versions.node === 'string'; + + const SAFARI = + global.navigator && + /^((?!chrome|android).)*safari/i.test(global.navigator.userAgent); + + if (NODE_JS) { + // Unlike browsers, Node doesn't require us to do a periodic setTimeout + // so we avoid the overhead. + return nodeQueueMicrotaskImpl(global); + } else if ( + SAFARI || + j$.util.isUndefined(global.MessageChannel) /* tests */ + ) { + // queueMicrotask is dramatically faster than MessageChannel in Safari, + // at least through version 16. + // Some of our own integration tests provide a mock queueMicrotask in all + // environments because it's simpler to mock than MessageChannel. + return browserQueueMicrotaskImpl(global); + } else { + // MessageChannel is faster than queueMicrotask in supported browsers + // other than Safari. + return messageChannelImpl(global); + } + } + + return getClearStack; +}; + +getJasmineRequireObj().Clock = function() { + /* global process */ + const NODE_JS = + typeof process !== 'undefined' && + process.versions && + typeof process.versions.node === 'string'; + + /** + * @class Clock + * @since 1.3.0 + * @classdesc Jasmine's mock clock is used when testing time dependent code.
+ * _Note:_ Do not construct this directly. You can get the current clock with + * {@link jasmine.clock}. + * @hideconstructor + */ + function Clock(global, delayedFunctionSchedulerFactory, mockDate) { + const realTimingFunctions = { + setTimeout: global.setTimeout, + clearTimeout: global.clearTimeout, + setInterval: global.setInterval, + clearInterval: global.clearInterval + }; + const fakeTimingFunctions = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setInterval: setInterval, + clearInterval: clearInterval + }; + let installed = false; + let delayedFunctionScheduler; + let timer; + + this.FakeTimeout = FakeTimeout; + + /** + * Install the mock clock over the built-in methods. + * @name Clock#install + * @since 2.0.0 + * @function + * @return {Clock} + */ + this.install = function() { + if (!originalTimingFunctionsIntact()) { + throw new Error( + 'Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?' + ); + } + replace(global, fakeTimingFunctions); + timer = fakeTimingFunctions; + delayedFunctionScheduler = delayedFunctionSchedulerFactory(); + installed = true; + + return this; + }; + + /** + * Uninstall the mock clock, returning the built-in methods to their places. + * @name Clock#uninstall + * @since 2.0.0 + * @function + */ + this.uninstall = function() { + delayedFunctionScheduler = null; + mockDate.uninstall(); + replace(global, realTimingFunctions); + + timer = realTimingFunctions; + installed = false; + }; + + /** + * Execute a function with a mocked Clock + * + * The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes. + * @name Clock#withMock + * @since 2.3.0 + * @function + * @param {Function} closure The function to be called. + */ + this.withMock = function(closure) { + this.install(); + try { + closure(); + } finally { + this.uninstall(); + } + }; + + /** + * Instruct the installed Clock to also mock the date returned by `new Date()` + * @name Clock#mockDate + * @since 2.1.0 + * @function + * @param {Date} [initialDate=now] The `Date` to provide. + */ + this.mockDate = function(initialDate) { + mockDate.install(initialDate); + }; + + this.setTimeout = function(fn, delay, params) { + return Function.prototype.apply.apply(timer.setTimeout, [ + global, + arguments + ]); + }; + + this.setInterval = function(fn, delay, params) { + return Function.prototype.apply.apply(timer.setInterval, [ + global, + arguments + ]); + }; + + this.clearTimeout = function(id) { + return Function.prototype.call.apply(timer.clearTimeout, [global, id]); + }; + + this.clearInterval = function(id) { + return Function.prototype.call.apply(timer.clearInterval, [global, id]); + }; + + /** + * Tick the Clock forward, running any enqueued timeouts along the way + * @name Clock#tick + * @since 1.3.0 + * @function + * @param {int} millis The number of milliseconds to tick. + */ + this.tick = function(millis) { + if (installed) { + delayedFunctionScheduler.tick(millis, function(millis) { + mockDate.tick(millis); + }); + } else { + throw new Error( + 'Mock clock is not installed, use jasmine.clock().install()' + ); + } + }; + + return this; + + function originalTimingFunctionsIntact() { + return ( + global.setTimeout === realTimingFunctions.setTimeout && + global.clearTimeout === realTimingFunctions.clearTimeout && + global.setInterval === realTimingFunctions.setInterval && + global.clearInterval === realTimingFunctions.clearInterval + ); + } + + function replace(dest, source) { + for (const prop in source) { + dest[prop] = source[prop]; + } + } + + function setTimeout(fn, delay) { + if (!NODE_JS) { + return delayedFunctionScheduler.scheduleFunction( + fn, + delay, + argSlice(arguments, 2) + ); + } + + const timeout = new FakeTimeout(); + + delayedFunctionScheduler.scheduleFunction( + fn, + delay, + argSlice(arguments, 2), + false, + timeout + ); + + return timeout; + } + + function clearTimeout(id) { + return delayedFunctionScheduler.removeFunctionWithId(id); + } + + function setInterval(fn, interval) { + if (!NODE_JS) { + return delayedFunctionScheduler.scheduleFunction( + fn, + interval, + argSlice(arguments, 2), + true + ); + } + + const timeout = new FakeTimeout(); + + delayedFunctionScheduler.scheduleFunction( + fn, + interval, + argSlice(arguments, 2), + true, + timeout + ); + + return timeout; + } + + function clearInterval(id) { + return delayedFunctionScheduler.removeFunctionWithId(id); + } + + function argSlice(argsObj, n) { + return Array.prototype.slice.call(argsObj, n); + } + } + + /** + * Mocks Node.js Timeout class + */ + function FakeTimeout() {} + + FakeTimeout.prototype.ref = function() { + return this; + }; + + FakeTimeout.prototype.unref = function() { + return this; + }; + + return Clock; +}; + +getJasmineRequireObj().CompleteOnFirstErrorSkipPolicy = function(j$) { + function CompleteOnFirstErrorSkipPolicy(queueableFns) { + this.queueableFns_ = queueableFns; + this.erroredFnIx_ = null; + } + + CompleteOnFirstErrorSkipPolicy.prototype.skipTo = function(lastRanFnIx) { + let i; + + for ( + i = lastRanFnIx + 1; + i < this.queueableFns_.length && this.shouldSkip_(i); + i++ + ) {} + return i; + }; + + CompleteOnFirstErrorSkipPolicy.prototype.fnErrored = function(fnIx) { + this.erroredFnIx_ = fnIx; + }; + + CompleteOnFirstErrorSkipPolicy.prototype.shouldSkip_ = function(fnIx) { + if (this.erroredFnIx_ === null) { + return false; + } + + const fn = this.queueableFns_[fnIx]; + const candidateSuite = fn.suite; + const errorSuite = this.queueableFns_[this.erroredFnIx_].suite; + const wasCleanupFn = + fn.type === 'afterEach' || + fn.type === 'afterAll' || + fn.type === 'specCleanup'; + return ( + !wasCleanupFn || + (candidateSuite && isDescendent(candidateSuite, errorSuite)) + ); + }; + + function isDescendent(candidate, ancestor) { + if (!candidate.parentSuite) { + return false; + } else if (candidate.parentSuite === ancestor) { + return true; + } else { + return isDescendent(candidate.parentSuite, ancestor); + } + } + + return CompleteOnFirstErrorSkipPolicy; +}; + +// Warning: don't add "use strict" to this file. Doing so potentially changes +// the behavior of user code that does things like setTimeout("var x = 1;") +// while the mock clock is installed. +getJasmineRequireObj().DelayedFunctionScheduler = function(j$) { + function DelayedFunctionScheduler() { + this.scheduledLookup_ = []; + this.scheduledFunctions_ = {}; + this.currentTime_ = 0; + this.delayedFnCount_ = 0; + this.deletedKeys_ = []; + + this.tick = function(millis, tickDate) { + millis = millis || 0; + const endTime = this.currentTime_ + millis; + + this.runScheduledFunctions_(endTime, tickDate); + }; + + this.scheduleFunction = function( + funcToCall, + millis, + params, + recurring, + timeoutKey, + runAtMillis + ) { + let f; + if (typeof funcToCall === 'string') { + // setTimeout("some code") and setInterval("some code") are legal, if + // not recommended. We don't do that ourselves, but user code might. + // This allows such code to work when the mock clock is installed. + f = function() { + // eslint-disable-next-line no-eval + return eval(funcToCall); + }; + } else { + f = funcToCall; + } + + millis = millis || 0; + timeoutKey = timeoutKey || ++this.delayedFnCount_; + runAtMillis = runAtMillis || this.currentTime_ + millis; + + const funcToSchedule = { + runAtMillis: runAtMillis, + funcToCall: f, + recurring: recurring, + params: params, + timeoutKey: timeoutKey, + millis: millis + }; + + if (runAtMillis in this.scheduledFunctions_) { + this.scheduledFunctions_[runAtMillis].push(funcToSchedule); + } else { + this.scheduledFunctions_[runAtMillis] = [funcToSchedule]; + this.scheduledLookup_.push(runAtMillis); + this.scheduledLookup_.sort(function(a, b) { + return a - b; + }); + } + + return timeoutKey; + }; + + this.removeFunctionWithId = function(timeoutKey) { + this.deletedKeys_.push(timeoutKey); + + for (const runAtMillis in this.scheduledFunctions_) { + const funcs = this.scheduledFunctions_[runAtMillis]; + const i = indexOfFirstToPass(funcs, function(func) { + return func.timeoutKey === timeoutKey; + }); + + if (i > -1) { + if (funcs.length === 1) { + delete this.scheduledFunctions_[runAtMillis]; + this.deleteFromLookup_(runAtMillis); + } else { + funcs.splice(i, 1); + } + + // intervals get rescheduled when executed, so there's never more + // than a single scheduled function with a given timeoutKey + break; + } + } + }; + + return this; + } + + DelayedFunctionScheduler.prototype.runScheduledFunctions_ = function( + endTime, + tickDate + ) { + tickDate = tickDate || function() {}; + if ( + this.scheduledLookup_.length === 0 || + this.scheduledLookup_[0] > endTime + ) { + if (endTime >= this.currentTime_) { + tickDate(endTime - this.currentTime_); + this.currentTime_ = endTime; + } + return; + } + + do { + this.deletedKeys_ = []; + const newCurrentTime = this.scheduledLookup_.shift(); + if (newCurrentTime >= this.currentTime_) { + tickDate(newCurrentTime - this.currentTime_); + this.currentTime_ = newCurrentTime; + } + + const funcsToRun = this.scheduledFunctions_[this.currentTime_]; + + delete this.scheduledFunctions_[this.currentTime_]; + + for (const fn of funcsToRun) { + if (fn.recurring) { + this.reschedule_(fn); + } + } + + for (const fn of funcsToRun) { + if (this.deletedKeys_.includes(fn.timeoutKey)) { + // skip a timeoutKey deleted whilst we were running + return; + } + fn.funcToCall.apply(null, fn.params || []); + } + this.deletedKeys_ = []; + } while ( + this.scheduledLookup_.length > 0 && + // checking first if we're out of time prevents setTimeout(0) + // scheduled in a funcToRun from forcing an extra iteration + this.currentTime_ !== endTime && + this.scheduledLookup_[0] <= endTime + ); + + // ran out of functions to call, but still time left on the clock + if (endTime >= this.currentTime_) { + tickDate(endTime - this.currentTime_); + this.currentTime_ = endTime; + } + }; + + DelayedFunctionScheduler.prototype.reschedule_ = function(scheduledFn) { + this.scheduleFunction( + scheduledFn.funcToCall, + scheduledFn.millis, + scheduledFn.params, + true, + scheduledFn.timeoutKey, + scheduledFn.runAtMillis + scheduledFn.millis + ); + }; + + DelayedFunctionScheduler.prototype.deleteFromLookup_ = function(key) { + const value = Number(key); + const i = indexOfFirstToPass(this.scheduledLookup_, function(millis) { + return millis === value; + }); + + if (i > -1) { + this.scheduledLookup_.splice(i, 1); + } + }; + + function indexOfFirstToPass(array, testFn) { + let index = -1; + + for (let i = 0; i < array.length; ++i) { + if (testFn(array[i])) { + index = i; + break; + } + } + + return index; + } + + return DelayedFunctionScheduler; +}; + +getJasmineRequireObj().Deprecator = function(j$) { + function Deprecator(topSuite) { + this.topSuite_ = topSuite; + this.verbose_ = false; + this.toSuppress_ = []; + } + + const verboseNote = + 'Note: This message will be shown only once. Set the verboseDeprecations ' + + 'config property to true to see every occurrence.'; + + Deprecator.prototype.verboseDeprecations = function(enabled) { + this.verbose_ = enabled; + }; + + // runnable is a spec or a suite. + // deprecation is a string or an Error. + // See Env#deprecated for a description of the options argument. + Deprecator.prototype.addDeprecationWarning = function( + runnable, + deprecation, + options + ) { + options = options || {}; + + if (!this.verbose_ && !j$.isError_(deprecation)) { + if (this.toSuppress_.indexOf(deprecation) !== -1) { + return; + } + this.toSuppress_.push(deprecation); + } + + this.log_(runnable, deprecation, options); + this.report_(runnable, deprecation, options); + }; + + Deprecator.prototype.log_ = function(runnable, deprecation, options) { + if (j$.isError_(deprecation)) { + console.error(deprecation); + return; + } + + let context; + + if (runnable === this.topSuite_ || options.ignoreRunnable) { + context = ''; + } else if (runnable.children) { + context = ' (in suite: ' + runnable.getFullName() + ')'; + } else { + context = ' (in spec: ' + runnable.getFullName() + ')'; + } + + if (!options.omitStackTrace) { + context += '\n' + this.stackTrace_(); + } + + if (!this.verbose_) { + context += '\n' + verboseNote; + } + + console.error('DEPRECATION: ' + deprecation + context); + }; + + Deprecator.prototype.stackTrace_ = function() { + const formatter = new j$.ExceptionFormatter(); + return formatter.stack(j$.util.errorWithStack()).replace(/^Error\n/m, ''); + }; + + Deprecator.prototype.report_ = function(runnable, deprecation, options) { + if (options.ignoreRunnable) { + runnable = this.topSuite_; + } + + if (j$.isError_(deprecation)) { + runnable.addDeprecationWarning(deprecation); + return; + } + + if (!this.verbose_) { + deprecation += '\n' + verboseNote; + } + + runnable.addDeprecationWarning({ + message: deprecation, + omitStackTrace: options.omitStackTrace || false + }); + }; + + return Deprecator; +}; + +getJasmineRequireObj().errors = function() { + function ExpectationFailed() {} + + ExpectationFailed.prototype = new Error(); + ExpectationFailed.prototype.constructor = ExpectationFailed; + + return { + ExpectationFailed: ExpectationFailed + }; +}; + +getJasmineRequireObj().ExceptionFormatter = function(j$) { + const ignoredProperties = [ + 'name', + 'message', + 'stack', + 'fileName', + 'sourceURL', + 'line', + 'lineNumber', + 'column', + 'description', + 'jasmineMessage' + ]; + + function ExceptionFormatter(options) { + const jasmineFile = + (options && options.jasmineFile) || j$.util.jasmineFile(); + this.message = function(error) { + let message = ''; + + if (error.jasmineMessage) { + message += error.jasmineMessage; + } else if (error.name && error.message) { + message += error.name + ': ' + error.message; + } else if (error.message) { + message += error.message; + } else { + message += error.toString() + ' thrown'; + } + + if (error.fileName || error.sourceURL) { + message += ' in ' + (error.fileName || error.sourceURL); + } + + if (error.line || error.lineNumber) { + message += ' (line ' + (error.line || error.lineNumber) + ')'; + } + + return message; + }; + + this.stack = function(error, { omitMessage } = {}) { + if (!error || !error.stack) { + return null; + } + + const lines = this.stack_(error, { + messageHandling: omitMessage ? 'omit' : undefined + }); + return lines.join('\n'); + }; + + // messageHandling can be falsy (unspecified), 'omit', or 'require' + this.stack_ = function(error, { messageHandling }) { + let lines = formatProperties(error).split('\n'); + + if (lines[lines.length - 1] === '') { + lines.pop(); + } + + const stackTrace = new j$.StackTrace(error); + lines = lines.concat(filterJasmine(stackTrace)); + + if (messageHandling === 'require') { + lines.unshift(stackTrace.message || 'Error: ' + error.message); + } else if (messageHandling !== 'omit' && stackTrace.message) { + lines.unshift(stackTrace.message); + } + + if (error.cause && error.cause instanceof Error) { + const substack = this.stack_(error.cause, { + messageHandling: 'require' + }); + substack[0] = 'Caused by: ' + substack[0]; + lines = lines.concat(substack); + } + + return lines; + }; + + function filterJasmine(stackTrace) { + const result = []; + const jasmineMarker = + stackTrace.style === 'webkit' ? '' : ' at '; + + stackTrace.frames.forEach(function(frame) { + if (frame.file !== jasmineFile) { + result.push(frame.raw); + } else if (result[result.length - 1] !== jasmineMarker) { + result.push(jasmineMarker); + } + }); + + return result; + } + + function formatProperties(error) { + if (!(error instanceof Object)) { + return; + } + + const result = {}; + let empty = true; + + for (const prop of Object.keys(error)) { + if (ignoredProperties.includes(prop)) { + continue; + } + result[prop] = error[prop]; + empty = false; + } + + if (!empty) { + return 'error properties: ' + j$.basicPrettyPrinter_(result) + '\n'; + } + + return ''; + } + } + + return ExceptionFormatter; +}; + +getJasmineRequireObj().Expectation = function(j$) { + /** + * Matchers that come with Jasmine out of the box. + * @namespace matchers + */ + function Expectation(options) { + this.expector = new j$.Expector(options); + + const customMatchers = options.customMatchers || {}; + for (const matcherName in customMatchers) { + this[matcherName] = wrapSyncCompare( + matcherName, + customMatchers[matcherName] + ); + } + } + + /** + * Add some context for an {@link expect} + * @function + * @name matchers#withContext + * @since 3.3.0 + * @param {String} message - Additional context to show when the matcher fails + * @return {matchers} + */ + Expectation.prototype.withContext = function withContext(message) { + return addFilter(this, new ContextAddingFilter(message)); + }; + + /** + * Invert the matcher following this {@link expect} + * @member + * @name matchers#not + * @since 1.3.0 + * @type {matchers} + * @example + * expect(something).not.toBe(true); + */ + Object.defineProperty(Expectation.prototype, 'not', { + get: function() { + return addFilter(this, syncNegatingFilter); + } + }); + + /** + * Asynchronous matchers that operate on an actual value which is a promise, + * and return a promise. + * + * Most async matchers will wait indefinitely for the promise to be resolved + * or rejected, resulting in a spec timeout if that never happens. If you + * expect that the promise will already be resolved or rejected at the time + * the matcher is called, you can use the {@link async-matchers#already} + * modifier to get a faster failure with a more helpful message. + * + * Note: Specs must await the result of each async matcher, return the + * promise returned by the matcher, or return a promise that's derived from + * the one returned by the matcher. Otherwise the matcher will not be + * evaluated before the spec completes. + * + * @example + * // Good + * await expectAsync(aPromise).toBeResolved(); + * @example + * // Good + * return expectAsync(aPromise).toBeResolved(); + * @example + * // Good + * return expectAsync(aPromise).toBeResolved() + * .then(function() { + * // more spec code + * }); + * @example + * // Bad + * expectAsync(aPromise).toBeResolved(); + * @namespace async-matchers + */ + function AsyncExpectation(options) { + this.expector = new j$.Expector(options); + + const customAsyncMatchers = options.customAsyncMatchers || {}; + for (const matcherName in customAsyncMatchers) { + this[matcherName] = wrapAsyncCompare( + matcherName, + customAsyncMatchers[matcherName] + ); + } + } + + /** + * Add some context for an {@link expectAsync} + * @function + * @name async-matchers#withContext + * @since 3.3.0 + * @param {String} message - Additional context to show when the async matcher fails + * @return {async-matchers} + */ + AsyncExpectation.prototype.withContext = function withContext(message) { + return addFilter(this, new ContextAddingFilter(message)); + }; + + /** + * Invert the matcher following this {@link expectAsync} + * @member + * @name async-matchers#not + * @type {async-matchers} + * @example + * await expectAsync(myPromise).not.toBeResolved(); + * @example + * return expectAsync(myPromise).not.toBeResolved(); + */ + Object.defineProperty(AsyncExpectation.prototype, 'not', { + get: function() { + return addFilter(this, asyncNegatingFilter); + } + }); + + /** + * Fail as soon as possible if the actual is pending. + * Otherwise evaluate the matcher. + * @member + * @name async-matchers#already + * @since 3.8.0 + * @type {async-matchers} + * @example + * await expectAsync(myPromise).already.toBeResolved(); + * @example + * return expectAsync(myPromise).already.toBeResolved(); + */ + Object.defineProperty(AsyncExpectation.prototype, 'already', { + get: function() { + return addFilter(this, expectSettledPromiseFilter); + } + }); + + function wrapSyncCompare(name, matcherFactory) { + return function() { + const result = this.expector.compare(name, matcherFactory, arguments); + this.expector.processResult(result); + }; + } + + function wrapAsyncCompare(name, matcherFactory) { + return function() { + // Capture the call stack here, before we go async, so that it will contain + // frames that are relevant to the user instead of just parts of Jasmine. + const errorForStack = j$.util.errorWithStack(); + + return this.expector + .compare(name, matcherFactory, arguments) + .then(result => { + this.expector.processResult(result, errorForStack); + }); + }; + } + + function addCoreMatchers(prototype, matchers, wrapper) { + for (const matcherName in matchers) { + const matcher = matchers[matcherName]; + prototype[matcherName] = wrapper(matcherName, matcher); + } + } + + function addFilter(source, filter) { + const result = Object.create(source); + result.expector = source.expector.addFilter(filter); + return result; + } + + function negatedFailureMessage(result, matcherName, args, matchersUtil) { + if (result.message) { + if (j$.isFunction_(result.message)) { + return result.message(); + } else { + return result.message; + } + } + + args = args.slice(); + args.unshift(true); + args.unshift(matcherName); + return matchersUtil.buildFailureMessage.apply(matchersUtil, args); + } + + function negate(result) { + result.pass = !result.pass; + return result; + } + + const syncNegatingFilter = { + selectComparisonFunc: function(matcher) { + function defaultNegativeCompare() { + return negate(matcher.compare.apply(null, arguments)); + } + + return matcher.negativeCompare || defaultNegativeCompare; + }, + buildFailureMessage: negatedFailureMessage + }; + + const asyncNegatingFilter = { + selectComparisonFunc: function(matcher) { + function defaultNegativeCompare() { + return matcher.compare.apply(this, arguments).then(negate); + } + + return matcher.negativeCompare || defaultNegativeCompare; + }, + buildFailureMessage: negatedFailureMessage + }; + + const expectSettledPromiseFilter = { + selectComparisonFunc: function(matcher) { + return function(actual) { + const matcherArgs = arguments; + + return j$.isPending_(actual).then(function(isPending) { + if (isPending) { + return { + pass: false, + message: + 'Expected a promise to be settled (via ' + + 'expectAsync(...).already) but it was pending.' + }; + } else { + return matcher.compare.apply(null, matcherArgs); + } + }); + }; + } + }; + + function ContextAddingFilter(message) { + this.message = message; + } + + ContextAddingFilter.prototype.modifyFailureMessage = function(msg) { + if (msg.indexOf('\n') === -1) { + return this.message + ': ' + msg; + } else { + return this.message + ':\n' + indent(msg); + } + }; + + function indent(s) { + return s.replace(/^/gm, ' '); + } + + return { + factory: function(options) { + return new Expectation(options || {}); + }, + addCoreMatchers: function(matchers) { + addCoreMatchers(Expectation.prototype, matchers, wrapSyncCompare); + }, + asyncFactory: function(options) { + return new AsyncExpectation(options || {}); + }, + addAsyncCoreMatchers: function(matchers) { + addCoreMatchers(AsyncExpectation.prototype, matchers, wrapAsyncCompare); + } + }; +}; + +getJasmineRequireObj().ExpectationFilterChain = function() { + function ExpectationFilterChain(maybeFilter, prev) { + this.filter_ = maybeFilter; + this.prev_ = prev; + } + + ExpectationFilterChain.prototype.addFilter = function(filter) { + return new ExpectationFilterChain(filter, this); + }; + + ExpectationFilterChain.prototype.selectComparisonFunc = function(matcher) { + return this.callFirst_('selectComparisonFunc', arguments).result; + }; + + ExpectationFilterChain.prototype.buildFailureMessage = function( + result, + matcherName, + args, + matchersUtil + ) { + return this.callFirst_('buildFailureMessage', arguments).result; + }; + + ExpectationFilterChain.prototype.modifyFailureMessage = function(msg) { + const result = this.callFirst_('modifyFailureMessage', arguments).result; + return result || msg; + }; + + ExpectationFilterChain.prototype.callFirst_ = function(fname, args) { + if (this.prev_) { + const prevResult = this.prev_.callFirst_(fname, args); + + if (prevResult.found) { + return prevResult; + } + } + + if (this.filter_ && this.filter_[fname]) { + return { + found: true, + result: this.filter_[fname].apply(this.filter_, args) + }; + } + + return { found: false }; + }; + + return ExpectationFilterChain; +}; + +getJasmineRequireObj().Expector = function(j$) { + function Expector(options) { + this.matchersUtil = options.matchersUtil || { + buildFailureMessage: function() {} + }; + this.actual = options.actual; + this.addExpectationResult = options.addExpectationResult || function() {}; + this.filters = new j$.ExpectationFilterChain(); + } + + Expector.prototype.instantiateMatcher = function( + matcherName, + matcherFactory, + args + ) { + this.matcherName = matcherName; + this.args = Array.prototype.slice.call(args, 0); + this.expected = this.args.slice(0); + + this.args.unshift(this.actual); + + const matcher = matcherFactory(this.matchersUtil); + + const comparisonFunc = this.filters.selectComparisonFunc(matcher); + return comparisonFunc || matcher.compare; + }; + + Expector.prototype.buildMessage = function(result) { + if (result.pass) { + return ''; + } + + const defaultMessage = () => { + if (!result.message) { + const args = this.args.slice(); + args.unshift(false); + args.unshift(this.matcherName); + return this.matchersUtil.buildFailureMessage.apply( + this.matchersUtil, + args + ); + } else if (j$.isFunction_(result.message)) { + return result.message(); + } else { + return result.message; + } + }; + + const msg = this.filters.buildFailureMessage( + result, + this.matcherName, + this.args, + this.matchersUtil, + defaultMessage + ); + return this.filters.modifyFailureMessage(msg || defaultMessage()); + }; + + Expector.prototype.compare = function(matcherName, matcherFactory, args) { + const matcherCompare = this.instantiateMatcher( + matcherName, + matcherFactory, + args + ); + return matcherCompare.apply(null, this.args); + }; + + Expector.prototype.addFilter = function(filter) { + const result = Object.create(this); + result.filters = this.filters.addFilter(filter); + return result; + }; + + Expector.prototype.processResult = function(result, errorForStack) { + const message = this.buildMessage(result); + + if (this.expected.length === 1) { + this.expected = this.expected[0]; + } + + this.addExpectationResult(result.pass, { + matcherName: this.matcherName, + passed: result.pass, + message: message, + error: errorForStack ? undefined : result.error, + errorForStack: errorForStack || undefined, + actual: this.actual, + expected: this.expected // TODO: this may need to be arrayified/sliced + }); + }; + + return Expector; +}; + +getJasmineRequireObj().formatErrorMsg = function() { + function generateErrorMsg(domain, usage) { + const usageDefinition = usage ? '\nUsage: ' + usage : ''; + + return function errorMsg(msg) { + return domain + ' : ' + msg + usageDefinition; + }; + } + + return generateErrorMsg; +}; + +getJasmineRequireObj().GlobalErrors = function(j$) { + function GlobalErrors(global) { + global = global || j$.getGlobal(); + + const handlers = []; + let overrideHandler = null, + onRemoveOverrideHandler = null; + + function onBrowserError(event) { + dispatchBrowserError(event.error, event); + } + + function dispatchBrowserError(error, event) { + if (overrideHandler) { + overrideHandler(error); + return; + } + + const handler = handlers[handlers.length - 1]; + + if (handler) { + handler(error, event); + } else { + throw error; + } + } + + this.originalHandlers = {}; + this.jasmineHandlers = {}; + this.installOne_ = function installOne_(errorType, jasmineMessage) { + function taggedOnError(error) { + if (j$.isError_(error)) { + error.jasmineMessage = jasmineMessage + ': ' + error; + } else { + let substituteMsg; + + if (error) { + substituteMsg = jasmineMessage + ': ' + error; + } else { + substituteMsg = jasmineMessage + ' with no error or message'; + } + + if (errorType === 'unhandledRejection') { + substituteMsg += + '\n' + + '(Tip: to get a useful stack trace, use ' + + 'Promise.reject(new Error(...)) instead of Promise.reject(' + + (error ? '...' : '') + + ').)'; + } + + error = new Error(substituteMsg); + } + + const handler = handlers[handlers.length - 1]; + + if (overrideHandler) { + overrideHandler(error); + return; + } + + if (handler) { + handler(error); + } else { + throw error; + } + } + + this.originalHandlers[errorType] = global.process.listeners(errorType); + this.jasmineHandlers[errorType] = taggedOnError; + + global.process.removeAllListeners(errorType); + global.process.on(errorType, taggedOnError); + + this.uninstall = function uninstall() { + const errorTypes = Object.keys(this.originalHandlers); + for (const errorType of errorTypes) { + global.process.removeListener( + errorType, + this.jasmineHandlers[errorType] + ); + + for (let i = 0; i < this.originalHandlers[errorType].length; i++) { + global.process.on(errorType, this.originalHandlers[errorType][i]); + } + delete this.originalHandlers[errorType]; + delete this.jasmineHandlers[errorType]; + } + }; + }; + + this.install = function install() { + if ( + global.process && + global.process.listeners && + j$.isFunction_(global.process.on) + ) { + this.installOne_('uncaughtException', 'Uncaught exception'); + this.installOne_('unhandledRejection', 'Unhandled promise rejection'); + } else { + global.addEventListener('error', onBrowserError); + + const browserRejectionHandler = function browserRejectionHandler( + event + ) { + if (j$.isError_(event.reason)) { + event.reason.jasmineMessage = + 'Unhandled promise rejection: ' + event.reason; + dispatchBrowserError(event.reason, event); + } else { + dispatchBrowserError( + 'Unhandled promise rejection: ' + event.reason, + event + ); + } + }; + + global.addEventListener('unhandledrejection', browserRejectionHandler); + + this.uninstall = function uninstall() { + global.removeEventListener('error', onBrowserError); + global.removeEventListener( + 'unhandledrejection', + browserRejectionHandler + ); + }; + } + }; + + // The listener at the top of the stack will be called with two arguments: + // the error and the event. Either of them may be falsy. + // The error will normally be provided, but will be falsy in the case of + // some browser load-time errors. The event will normally be provided in + // browsers but will be falsy in Node. + // Listeners that are pushed after spec files have been loaded should be + // able to just use the error parameter. + this.pushListener = function pushListener(listener) { + handlers.push(listener); + }; + + this.popListener = function popListener(listener) { + if (!listener) { + throw new Error('popListener expects a listener'); + } + + handlers.pop(); + }; + + this.setOverrideListener = function(listener, onRemove) { + if (overrideHandler) { + throw new Error("Can't set more than one override listener at a time"); + } + + overrideHandler = listener; + onRemoveOverrideHandler = onRemove; + }; + + this.removeOverrideListener = function() { + if (onRemoveOverrideHandler) { + onRemoveOverrideHandler(); + } + + overrideHandler = null; + onRemoveOverrideHandler = null; + }; + } + + return GlobalErrors; +}; + +getJasmineRequireObj().toBePending = function(j$) { + /** + * Expect a promise to be pending, i.e. the promise is neither resolved nor rejected. + * @function + * @async + * @name async-matchers#toBePending + * @since 3.6 + * @example + * await expectAsync(aPromise).toBePending(); + */ + return function toBePending() { + return { + compare: function(actual) { + if (!j$.isPromiseLike(actual)) { + throw new Error('Expected toBePending to be called on a promise.'); + } + const want = {}; + return Promise.race([actual, Promise.resolve(want)]).then( + function(got) { + return { pass: want === got }; + }, + function() { + return { pass: false }; + } + ); + } + }; + }; +}; + +getJasmineRequireObj().toBeRejected = function(j$) { + /** + * Expect a promise to be rejected. + * @function + * @async + * @name async-matchers#toBeRejected + * @since 3.1.0 + * @example + * await expectAsync(aPromise).toBeRejected(); + * @example + * return expectAsync(aPromise).toBeRejected(); + */ + return function toBeRejected() { + return { + compare: function(actual) { + if (!j$.isPromiseLike(actual)) { + throw new Error('Expected toBeRejected to be called on a promise.'); + } + return actual.then( + function() { + return { pass: false }; + }, + function() { + return { pass: true }; + } + ); + } + }; + }; +}; + +getJasmineRequireObj().toBeRejectedWith = function(j$) { + /** + * Expect a promise to be rejected with a value equal to the expected, using deep equality comparison. + * @function + * @async + * @name async-matchers#toBeRejectedWith + * @since 3.3.0 + * @param {Object} expected - Value that the promise is expected to be rejected with + * @example + * await expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); + * @example + * return expectAsync(aPromise).toBeRejectedWith({prop: 'value'}); + */ + return function toBeRejectedWith(matchersUtil) { + return { + compare: function(actualPromise, expectedValue) { + if (!j$.isPromiseLike(actualPromise)) { + throw new Error( + 'Expected toBeRejectedWith to be called on a promise.' + ); + } + + function prefix(passed) { + return ( + 'Expected a promise ' + + (passed ? 'not ' : '') + + 'to be rejected with ' + + matchersUtil.pp(expectedValue) + ); + } + + return actualPromise.then( + function() { + return { + pass: false, + message: prefix(false) + ' but it was resolved.' + }; + }, + function(actualValue) { + if (matchersUtil.equals(actualValue, expectedValue)) { + return { + pass: true, + message: prefix(true) + '.' + }; + } else { + return { + pass: false, + message: + prefix(false) + + ' but it was rejected with ' + + matchersUtil.pp(actualValue) + + '.' + }; + } + } + ); + } + }; + }; +}; + +getJasmineRequireObj().toBeRejectedWithError = function(j$) { + /** + * Expect a promise to be rejected with a value matched to the expected + * @function + * @async + * @name async-matchers#toBeRejectedWithError + * @since 3.5.0 + * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. + * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` + * @example + * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, 'Error message'); + * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError, /Error message/); + * await expectAsync(aPromise).toBeRejectedWithError(MyCustomError); + * await expectAsync(aPromise).toBeRejectedWithError('Error message'); + * return expectAsync(aPromise).toBeRejectedWithError(/Error message/); + */ + return function toBeRejectedWithError(matchersUtil) { + return { + compare: function(actualPromise, arg1, arg2) { + if (!j$.isPromiseLike(actualPromise)) { + throw new Error( + 'Expected toBeRejectedWithError to be called on a promise.' + ); + } + + const expected = getExpectedFromArgs(arg1, arg2, matchersUtil); + + return actualPromise.then( + function() { + return { + pass: false, + message: 'Expected a promise to be rejected but it was resolved.' + }; + }, + function(actualValue) { + return matchError(actualValue, expected, matchersUtil); + } + ); + } + }; + }; + + function matchError(actual, expected, matchersUtil) { + if (!j$.isError_(actual)) { + return fail(expected, 'rejected with ' + matchersUtil.pp(actual)); + } + + if (!(actual instanceof expected.error)) { + return fail( + expected, + 'rejected with type ' + j$.fnNameFor(actual.constructor) + ); + } + + const actualMessage = actual.message; + + if ( + actualMessage === expected.message || + typeof expected.message === 'undefined' + ) { + return pass(expected); + } + + if ( + expected.message instanceof RegExp && + expected.message.test(actualMessage) + ) { + return pass(expected); + } + + return fail(expected, 'rejected with ' + matchersUtil.pp(actual)); + } + + function pass(expected) { + return { + pass: true, + message: + 'Expected a promise not to be rejected with ' + + expected.printValue + + ', but it was.' + }; + } + + function fail(expected, message) { + return { + pass: false, + message: + 'Expected a promise to be rejected with ' + + expected.printValue + + ' but it was ' + + message + + '.' + }; + } + + function getExpectedFromArgs(arg1, arg2, matchersUtil) { + let error, message; + + if (isErrorConstructor(arg1)) { + error = arg1; + message = arg2; + } else { + error = Error; + message = arg1; + } + + return { + error: error, + message: message, + printValue: + j$.fnNameFor(error) + + (typeof message === 'undefined' ? '' : ': ' + matchersUtil.pp(message)) + }; + } + + function isErrorConstructor(value) { + return ( + typeof value === 'function' && + (value === Error || j$.isError_(value.prototype)) + ); + } +}; + +getJasmineRequireObj().toBeResolved = function(j$) { + /** + * Expect a promise to be resolved. + * @function + * @async + * @name async-matchers#toBeResolved + * @since 3.1.0 + * @example + * await expectAsync(aPromise).toBeResolved(); + * @example + * return expectAsync(aPromise).toBeResolved(); + */ + return function toBeResolved(matchersUtil) { + return { + compare: function(actual) { + if (!j$.isPromiseLike(actual)) { + throw new Error('Expected toBeResolved to be called on a promise.'); + } + + return actual.then( + function() { + return { pass: true }; + }, + function(e) { + return { + pass: false, + message: + 'Expected a promise to be resolved but it was ' + + 'rejected with ' + + matchersUtil.pp(e) + + '.' + }; + } + ); + } + }; + }; +}; + +getJasmineRequireObj().toBeResolvedTo = function(j$) { + /** + * Expect a promise to be resolved to a value equal to the expected, using deep equality comparison. + * @function + * @async + * @name async-matchers#toBeResolvedTo + * @since 3.1.0 + * @param {Object} expected - Value that the promise is expected to resolve to + * @example + * await expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); + * @example + * return expectAsync(aPromise).toBeResolvedTo({prop: 'value'}); + */ + return function toBeResolvedTo(matchersUtil) { + return { + compare: function(actualPromise, expectedValue) { + if (!j$.isPromiseLike(actualPromise)) { + throw new Error('Expected toBeResolvedTo to be called on a promise.'); + } + + function prefix(passed) { + return ( + 'Expected a promise ' + + (passed ? 'not ' : '') + + 'to be resolved to ' + + matchersUtil.pp(expectedValue) + ); + } + + return actualPromise.then( + function(actualValue) { + if (matchersUtil.equals(actualValue, expectedValue)) { + return { + pass: true, + message: prefix(true) + '.' + }; + } else { + return { + pass: false, + message: + prefix(false) + + ' but it was resolved to ' + + matchersUtil.pp(actualValue) + + '.' + }; + } + }, + function(e) { + return { + pass: false, + message: + prefix(false) + + ' but it was rejected with ' + + matchersUtil.pp(e) + + '.' + }; + } + ); + } + }; + }; +}; + +getJasmineRequireObj().DiffBuilder = function(j$) { + class DiffBuilder { + constructor(config) { + this.prettyPrinter_ = + (config || {}).prettyPrinter || j$.makePrettyPrinter(); + this.mismatches_ = new j$.MismatchTree(); + this.path_ = new j$.ObjectPath(); + this.actualRoot_ = undefined; + this.expectedRoot_ = undefined; + } + + setRoots(actual, expected) { + this.actualRoot_ = actual; + this.expectedRoot_ = expected; + } + + recordMismatch(formatter) { + this.mismatches_.add(this.path_, formatter); + } + + getMessage() { + const messages = []; + + this.mismatches_.traverse((path, isLeaf, formatter) => { + const { actual, expected } = this.dereferencePath_(path); + + if (formatter) { + messages.push(formatter(actual, expected, path, this.prettyPrinter_)); + return true; + } + + const actualCustom = this.prettyPrinter_.customFormat_(actual); + const expectedCustom = this.prettyPrinter_.customFormat_(expected); + const useCustom = !( + j$.util.isUndefined(actualCustom) && + j$.util.isUndefined(expectedCustom) + ); + + if (useCustom) { + messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path)); + return false; // don't recurse further + } + + if (isLeaf) { + messages.push(this.defaultFormatter_(actual, expected, path)); + } + + return true; + }); + + return messages.join('\n'); + } + + withPath(pathComponent, block) { + const oldPath = this.path_; + this.path_ = this.path_.add(pathComponent); + block(); + this.path_ = oldPath; + } + + dereferencePath_(objectPath) { + let actual = this.actualRoot_; + let expected = this.expectedRoot_; + + const handleAsymmetricExpected = () => { + if ( + j$.isAsymmetricEqualityTester_(expected) && + j$.isFunction_(expected.valuesForDiff_) + ) { + const asymmetricResult = expected.valuesForDiff_( + actual, + this.prettyPrinter_ + ); + expected = asymmetricResult.self; + actual = asymmetricResult.other; + } + }; + + handleAsymmetricExpected(); + + for (const pc of objectPath.components) { + actual = actual[pc]; + expected = expected[pc]; + handleAsymmetricExpected(); + } + + return { actual: actual, expected: expected }; + } + + defaultFormatter_(actual, expected, path) { + return wrapPrettyPrinted( + this.prettyPrinter_(actual), + this.prettyPrinter_(expected), + path + ); + } + } + + function wrapPrettyPrinted(actual, expected, path) { + return ( + 'Expected ' + + path + + (path.depth() ? ' = ' : '') + + actual + + ' to equal ' + + expected + + '.' + ); + } + + return DiffBuilder; +}; + +getJasmineRequireObj().MatchersUtil = function(j$) { + /** + * @class MatchersUtil + * @classdesc Utilities for use in implementing matchers.
+ * _Note:_ Do not construct this directly. Jasmine will construct one and + * pass it to matchers and asymmetric equality testers. + * @hideconstructor + */ + function MatchersUtil(options) { + options = options || {}; + this.customTesters_ = options.customTesters || []; + /** + * Formats a value for use in matcher failure messages and similar contexts, + * taking into account the current set of custom value formatters. + * @function + * @name MatchersUtil#pp + * @since 3.6.0 + * @param {*} value The value to pretty-print + * @return {string} The pretty-printed value + */ + this.pp = options.pp || function() {}; + } + + /** + * Determines whether `haystack` contains `needle`, using the same comparison + * logic as {@link MatchersUtil#equals}. + * @function + * @name MatchersUtil#contains + * @since 2.0.0 + * @param {*} haystack The collection to search + * @param {*} needle The value to search for + * @returns {boolean} True if `needle` was found in `haystack` + */ + MatchersUtil.prototype.contains = function(haystack, needle) { + if (!haystack) { + return false; + } + + if (j$.isSet(haystack)) { + // Try .has() first. It should be faster in cases where + // needle === something in haystack. Fall back to .equals() comparison + // if that fails. + if (haystack.has(needle)) { + return true; + } + } + + if (j$.isIterable_(haystack) && !j$.isString_(haystack)) { + // Arrays, Sets, etc. + for (const candidate of haystack) { + if (this.equals(candidate, needle)) { + return true; + } + } + + return false; + } + + if (haystack.indexOf) { + // Mainly strings + return haystack.indexOf(needle) >= 0; + } + + if (j$.isNumber_(haystack.length)) { + // Objects that are shaped like arrays but aren't iterable + for (let i = 0; i < haystack.length; i++) { + if (this.equals(haystack[i], needle)) { + return true; + } + } + } + + return false; + }; + + MatchersUtil.prototype.buildFailureMessage = function() { + const args = Array.prototype.slice.call(arguments, 0), + matcherName = args[0], + isNot = args[1], + actual = args[2], + expected = args.slice(3), + englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { + return ' ' + s.toLowerCase(); + }); + + let message = + 'Expected ' + + this.pp(actual) + + (isNot ? ' not ' : ' ') + + englishyPredicate; + + if (expected.length > 0) { + for (let i = 0; i < expected.length; i++) { + if (i > 0) { + message += ','; + } + message += ' ' + this.pp(expected[i]); + } + } + + return message + '.'; + }; + + MatchersUtil.prototype.asymmetricDiff_ = function( + a, + b, + aStack, + bStack, + diffBuilder + ) { + if (j$.isFunction_(b.valuesForDiff_)) { + const values = b.valuesForDiff_(a, this.pp); + this.eq_(values.other, values.self, aStack, bStack, diffBuilder); + } else { + diffBuilder.recordMismatch(); + } + }; + + MatchersUtil.prototype.asymmetricMatch_ = function( + a, + b, + aStack, + bStack, + diffBuilder + ) { + const asymmetricA = j$.isAsymmetricEqualityTester_(a); + const asymmetricB = j$.isAsymmetricEqualityTester_(b); + + if (asymmetricA === asymmetricB) { + return undefined; + } + + let result; + + if (asymmetricA) { + result = a.asymmetricMatch(b, this); + if (!result) { + diffBuilder.recordMismatch(); + } + return result; + } + + if (asymmetricB) { + result = b.asymmetricMatch(a, this); + if (!result) { + this.asymmetricDiff_(a, b, aStack, bStack, diffBuilder); + } + return result; + } + }; + + /** + * Determines whether two values are deeply equal to each other. + * @function + * @name MatchersUtil#equals + * @since 2.0.0 + * @param {*} a The first value to compare + * @param {*} b The second value to compare + * @returns {boolean} True if the values are equal + */ + MatchersUtil.prototype.equals = function(a, b, diffBuilder) { + diffBuilder = diffBuilder || j$.NullDiffBuilder(); + diffBuilder.setRoots(a, b); + + return this.eq_(a, b, [], [], diffBuilder); + }; + + // Equality function lovingly adapted from isEqual in + // [Underscore](http://underscorejs.org) + MatchersUtil.prototype.eq_ = function(a, b, aStack, bStack, diffBuilder) { + let result = true; + + const asymmetricResult = this.asymmetricMatch_( + a, + b, + aStack, + bStack, + diffBuilder + ); + if (!j$.util.isUndefined(asymmetricResult)) { + return asymmetricResult; + } + + for (const tester of this.customTesters_) { + const customTesterResult = tester(a, b); + if (!j$.util.isUndefined(customTesterResult)) { + if (!customTesterResult) { + diffBuilder.recordMismatch(); + } + return customTesterResult; + } + } + + if (a instanceof Error && b instanceof Error) { + result = a.message == b.message; + if (!result) { + diffBuilder.recordMismatch(); + } + return result; + } + + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) { + result = a !== 0 || 1 / a == 1 / b; + if (!result) { + diffBuilder.recordMismatch(); + } + return result; + } + // A strict comparison is necessary because `null == undefined`. + if (a === null || b === null) { + result = a === b; + if (!result) { + diffBuilder.recordMismatch(); + } + return result; + } + const className = Object.prototype.toString.call(a); + if (className != Object.prototype.toString.call(b)) { + diffBuilder.recordMismatch(); + return false; + } + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + result = a == String(b); + if (!result) { + diffBuilder.recordMismatch(); + } + return result; + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + result = + a != +a ? b != +b : a === 0 && b === 0 ? 1 / a == 1 / b : a == +b; + if (!result) { + diffBuilder.recordMismatch(); + } + return result; + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + result = +a == +b; + if (!result) { + diffBuilder.recordMismatch(); + } + return result; + case '[object ArrayBuffer]': + // If we have an instance of ArrayBuffer the Uint8Array ctor + // will be defined as well + return this.eq_( + new Uint8Array(a), + new Uint8Array(b), + aStack, + bStack, + diffBuilder + ); + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return ( + a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase + ); + } + if (typeof a != 'object' || typeof b != 'object') { + diffBuilder.recordMismatch(); + return false; + } + + const aIsDomNode = j$.isDomNode(a); + const bIsDomNode = j$.isDomNode(b); + if (aIsDomNode && bIsDomNode) { + // At first try to use DOM3 method isEqualNode + result = a.isEqualNode(b); + if (!result) { + diffBuilder.recordMismatch(); + } + return result; + } + if (aIsDomNode || bIsDomNode) { + diffBuilder.recordMismatch(); + return false; + } + + const aIsPromise = j$.isPromise(a); + const bIsPromise = j$.isPromise(b); + if (aIsPromise && bIsPromise) { + return a === b; + } + + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + let length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) { + return bStack[length] == b; + } + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + let size = 0; + // Recursively compare objects and arrays. + // Compare array lengths to determine if a deep comparison is necessary. + if (className == '[object Array]') { + const aLength = a.length; + const bLength = b.length; + + diffBuilder.withPath('length', function() { + if (aLength !== bLength) { + diffBuilder.recordMismatch(); + result = false; + } + }); + + for (let i = 0; i < aLength || i < bLength; i++) { + diffBuilder.withPath(i, () => { + if (i >= bLength) { + diffBuilder.recordMismatch( + actualArrayIsLongerFormatter.bind(null, this.pp) + ); + result = false; + } else { + result = + this.eq_( + i < aLength ? a[i] : void 0, + i < bLength ? b[i] : void 0, + aStack, + bStack, + diffBuilder + ) && result; + } + }); + } + if (!result) { + return false; + } + } else if (j$.isMap(a) && j$.isMap(b)) { + if (a.size != b.size) { + diffBuilder.recordMismatch(); + return false; + } + + const keysA = []; + const keysB = []; + a.forEach(function(valueA, keyA) { + keysA.push(keyA); + }); + b.forEach(function(valueB, keyB) { + keysB.push(keyB); + }); + + // For both sets of keys, check they map to equal values in both maps. + // Keep track of corresponding keys (in insertion order) in order to handle asymmetric obj keys. + const mapKeys = [keysA, keysB]; + const cmpKeys = [keysB, keysA]; + for (let i = 0; result && i < mapKeys.length; i++) { + const mapIter = mapKeys[i]; + const cmpIter = cmpKeys[i]; + + for (let j = 0; result && j < mapIter.length; j++) { + const mapKey = mapIter[j]; + const cmpKey = cmpIter[j]; + const mapValueA = a.get(mapKey); + let mapValueB; + + // Only use the cmpKey when one of the keys is asymmetric and the corresponding key matches, + // otherwise explicitly look up the mapKey in the other Map since we want keys with unique + // obj identity (that are otherwise equal) to not match. + if ( + j$.isAsymmetricEqualityTester_(mapKey) || + (j$.isAsymmetricEqualityTester_(cmpKey) && + this.eq_(mapKey, cmpKey, aStack, bStack, j$.NullDiffBuilder())) + ) { + mapValueB = b.get(cmpKey); + } else { + mapValueB = b.get(mapKey); + } + result = this.eq_( + mapValueA, + mapValueB, + aStack, + bStack, + j$.NullDiffBuilder() + ); + } + } + + if (!result) { + diffBuilder.recordMismatch(); + return false; + } + } else if (j$.isSet(a) && j$.isSet(b)) { + if (a.size != b.size) { + diffBuilder.recordMismatch(); + return false; + } + + const valuesA = []; + a.forEach(function(valueA) { + valuesA.push(valueA); + }); + const valuesB = []; + b.forEach(function(valueB) { + valuesB.push(valueB); + }); + + // For both sets, check they are all contained in the other set + const setPairs = [[valuesA, valuesB], [valuesB, valuesA]]; + const stackPairs = [[aStack, bStack], [bStack, aStack]]; + for (let i = 0; result && i < setPairs.length; i++) { + const baseValues = setPairs[i][0]; + const otherValues = setPairs[i][1]; + const baseStack = stackPairs[i][0]; + const otherStack = stackPairs[i][1]; + // For each value in the base set... + for (const baseValue of baseValues) { + let found = false; + // ... test that it is present in the other set + for (let j = 0; !found && j < otherValues.length; j++) { + const otherValue = otherValues[j]; + const prevStackSize = baseStack.length; + // compare by value equality + found = this.eq_( + baseValue, + otherValue, + baseStack, + otherStack, + j$.NullDiffBuilder() + ); + if (!found && prevStackSize !== baseStack.length) { + baseStack.splice(prevStackSize); + otherStack.splice(prevStackSize); + } + } + result = result && found; + } + } + + if (!result) { + diffBuilder.recordMismatch(); + return false; + } + } else if (j$.isURL(a) && j$.isURL(b)) { + // URLs have no enumrable properties, so the default object comparison + // would consider any two URLs to be equal. + return a.toString() === b.toString(); + } else { + // Objects with different constructors are not equivalent, but `Object`s + // or `Array`s from different frames are. + const aCtor = a.constructor, + bCtor = b.constructor; + if ( + aCtor !== bCtor && + isFunction(aCtor) && + isFunction(bCtor) && + a instanceof aCtor && + b instanceof bCtor && + !(aCtor instanceof aCtor && bCtor instanceof bCtor) + ) { + diffBuilder.recordMismatch( + constructorsAreDifferentFormatter.bind(null, this.pp) + ); + return false; + } + } + + // Deep compare objects. + const aKeys = MatchersUtil.keys(a, className == '[object Array]'); + size = aKeys.length; + + // Ensure that both objects contain the same number of properties before comparing deep equality. + if (MatchersUtil.keys(b, className == '[object Array]').length !== size) { + diffBuilder.recordMismatch( + objectKeysAreDifferentFormatter.bind(null, this.pp) + ); + return false; + } + + for (const key of aKeys) { + // Deep compare each member + if (!j$.util.has(b, key)) { + diffBuilder.recordMismatch( + objectKeysAreDifferentFormatter.bind(null, this.pp) + ); + result = false; + continue; + } + + diffBuilder.withPath(key, () => { + if (!this.eq_(a[key], b[key], aStack, bStack, diffBuilder)) { + result = false; + } + }); + } + + if (!result) { + return false; + } + + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + + return result; + }; + + MatchersUtil.keys = function(obj, isArray) { + const allKeys = (function(o) { + const keys = []; + for (const key in o) { + if (j$.util.has(o, key)) { + keys.push(key); + } + } + + const symbols = Object.getOwnPropertySymbols(o); + for (const sym of symbols) { + if (o.propertyIsEnumerable(sym)) { + keys.push(sym); + } + } + + return keys; + })(obj); + + if (!isArray) { + return allKeys; + } + + if (allKeys.length === 0) { + return allKeys; + } + + const extraKeys = []; + for (const k of allKeys) { + if (typeof k === 'symbol' || !/^[0-9]+$/.test(k)) { + extraKeys.push(k); + } + } + + return extraKeys; + }; + + function isFunction(obj) { + return typeof obj === 'function'; + } + + // Returns an array of [k, v] pairs for eacch property that's in objA + // and not in objB. + function extraKeysAndValues(objA, objB) { + return MatchersUtil.keys(objA) + .filter(key => !j$.util.has(objB, key)) + .map(key => [key, objA[key]]); + } + + function objectKeysAreDifferentFormatter(pp, actual, expected, path) { + const missingProperties = extraKeysAndValues(expected, actual), + extraProperties = extraKeysAndValues(actual, expected), + missingPropertiesMessage = formatKeyValuePairs(pp, missingProperties), + extraPropertiesMessage = formatKeyValuePairs(pp, extraProperties), + messages = []; + + if (!path.depth()) { + path = 'object'; + } + + if (missingPropertiesMessage.length) { + messages.push( + 'Expected ' + path + ' to have properties' + missingPropertiesMessage + ); + } + + if (extraPropertiesMessage.length) { + messages.push( + 'Expected ' + path + ' not to have properties' + extraPropertiesMessage + ); + } + + return messages.join('\n'); + } + + function constructorsAreDifferentFormatter(pp, actual, expected, path) { + if (!path.depth()) { + path = 'object'; + } + + return ( + 'Expected ' + + path + + ' to be a kind of ' + + j$.fnNameFor(expected.constructor) + + ', but was ' + + pp(actual) + + '.' + ); + } + + function actualArrayIsLongerFormatter(pp, actual, expected, path) { + return ( + 'Unexpected ' + + path + + (path.depth() ? ' = ' : '') + + pp(actual) + + ' in array.' + ); + } + + function formatKeyValuePairs(pp, keyValuePairs) { + let formatted = ''; + + for (const [key, value] of keyValuePairs) { + formatted += '\n ' + key.toString() + ': ' + pp(value); + } + + return formatted; + } + + return MatchersUtil; +}; + +/** + * @interface AsymmetricEqualityTester + * @classdesc An asymmetric equality tester is an object that can match multiple + * objects. Examples include jasmine.any() and jasmine.stringMatching(). Jasmine + * includes a number of built-in asymmetric equality testers, such as + * {@link jasmine.objectContaining}. User-defined asymmetric equality testers are + * also supported. + * + * Asymmetric equality testers work with any matcher, including user-defined + * custom matchers, that uses {@link MatchersUtil#equals} or + * {@link MatchersUtil#contains}. + * + * @example + * function numberDivisibleBy(divisor) { + * return { + * asymmetricMatch: function(n) { + * return typeof n === 'number' && n % divisor === 0; + * }, + * jasmineToString: function() { + * return ``; + * } + * }; + * } + * + * const actual = { + * n: 2, + * otherFields: "don't care" + * }; + * + * expect(actual).toEqual(jasmine.objectContaining({n: numberDivisibleBy(2)})); + * @see custom_asymmetric_equality_testers + * @since 2.0.0 + */ +/** + * Determines whether a value matches this tester + * @function + * @name AsymmetricEqualityTester#asymmetricMatch + * @param value {any} The value to test + * @param matchersUtil {MatchersUtil} utilities for testing equality, etc + * @return {Boolean} + */ +/** + * Returns a string representation of this tester to use in matcher failure messages + * @function + * @name AsymmetricEqualityTester#jasmineToString + * @param pp {function} Function that takes a value and returns a pretty-printed representation + * @return {String} + */ + +getJasmineRequireObj().MismatchTree = function(j$) { + /* + To be able to apply custom object formatters at all possible levels of an + object graph, DiffBuilder needs to be able to know not just where the + mismatch occurred but also all ancestors of the mismatched value in both + the expected and actual object graphs. MismatchTree maintains that context + and provides it via the traverse method. + */ + class MismatchTree { + constructor(path) { + this.path = path || new j$.ObjectPath([]); + this.formatter = undefined; + this.children = []; + this.isMismatch = false; + } + + add(path, formatter) { + if (path.depth() === 0) { + this.formatter = formatter; + this.isMismatch = true; + } else { + const key = path.components[0]; + path = path.shift(); + let child = this.child(key); + + if (!child) { + child = new MismatchTree(this.path.add(key)); + this.children.push(child); + } + + child.add(path, formatter); + } + } + + traverse(visit) { + const hasChildren = this.children.length > 0; + + if (this.isMismatch || hasChildren) { + if (visit(this.path, !hasChildren, this.formatter)) { + for (const child of this.children) { + child.traverse(visit); + } + } + } + } + + child(key) { + return this.children.find(child => { + const pathEls = child.path.components; + return pathEls[pathEls.length - 1] === key; + }); + } + } + + return MismatchTree; +}; + +getJasmineRequireObj().nothing = function() { + /** + * {@link expect} nothing explicitly. + * @function + * @name matchers#nothing + * @since 2.8.0 + * @example + * expect().nothing(); + */ + function nothing() { + return { + compare: function() { + return { + pass: true + }; + } + }; + } + + return nothing; +}; + +getJasmineRequireObj().NullDiffBuilder = function(j$) { + return function() { + return { + withPath: function(_, block) { + block(); + }, + setRoots: function() {}, + recordMismatch: function() {} + }; + }; +}; + +getJasmineRequireObj().ObjectPath = function(j$) { + class ObjectPath { + constructor(components) { + this.components = components || []; + } + + toString() { + if (this.components.length) { + return '$' + this.components.map(formatPropertyAccess).join(''); + } else { + return ''; + } + } + + add(component) { + return new ObjectPath(this.components.concat([component])); + } + + shift() { + return new ObjectPath(this.components.slice(1)); + } + + depth() { + return this.components.length; + } + } + + function formatPropertyAccess(prop) { + if (typeof prop === 'number' || typeof prop === 'symbol') { + return '[' + prop.toString() + ']'; + } + + if (isValidIdentifier(prop)) { + return '.' + prop; + } + + return `['${prop}']`; + } + + function isValidIdentifier(string) { + return /^[A-Za-z\$_][A-Za-z0-9\$_]*$/.test(string); + } + + return ObjectPath; +}; + +getJasmineRequireObj().requireAsyncMatchers = function(jRequire, j$) { + const availableMatchers = [ + 'toBePending', + 'toBeResolved', + 'toBeRejected', + 'toBeResolvedTo', + 'toBeRejectedWith', + 'toBeRejectedWithError' + ], + matchers = {}; + + for (const name of availableMatchers) { + matchers[name] = jRequire[name](j$); + } + + return matchers; +}; + +getJasmineRequireObj().toBe = function(j$) { + /** + * {@link expect} the actual value to be `===` to the expected value. + * @function + * @name matchers#toBe + * @since 1.3.0 + * @param {Object} expected - The expected value to compare against. + * @example + * expect(thing).toBe(realThing); + */ + function toBe(matchersUtil) { + const tip = + ' Tip: To check for deep equality, use .toEqual() instead of .toBe().'; + + return { + compare: function(actual, expected) { + const result = { + pass: actual === expected + }; + + if (typeof expected === 'object') { + result.message = + matchersUtil.buildFailureMessage( + 'toBe', + result.pass, + actual, + expected + ) + tip; + } + + return result; + } + }; + } + + return toBe; +}; + +getJasmineRequireObj().toBeCloseTo = function() { + /** + * {@link expect} the actual value to be within a specified precision of the expected value. + * @function + * @name matchers#toBeCloseTo + * @since 1.3.0 + * @param {Object} expected - The expected value to compare against. + * @param {Number} [precision=2] - The number of decimal points to check. + * @example + * expect(number).toBeCloseTo(42.2, 3); + */ + function toBeCloseTo() { + return { + compare: function(actual, expected, precision) { + if (precision !== 0) { + precision = precision || 2; + } + + if (expected === null || actual === null) { + throw new Error( + 'Cannot use toBeCloseTo with null. Arguments evaluated to: ' + + 'expect(' + + actual + + ').toBeCloseTo(' + + expected + + ').' + ); + } + + // Infinity is close to Infinity and -Infinity is close to -Infinity, + // regardless of the precision. + if (expected === Infinity || expected === -Infinity) { + return { + pass: actual === expected + }; + } + + const pow = Math.pow(10, precision + 1); + const delta = Math.abs(expected - actual); + const maxDelta = Math.pow(10, -precision) / 2; + + return { + pass: Math.round(delta * pow) <= maxDelta * pow + }; + } + }; + } + + return toBeCloseTo; +}; + +getJasmineRequireObj().toBeDefined = function() { + /** + * {@link expect} the actual value to be defined. (Not `undefined`) + * @function + * @name matchers#toBeDefined + * @since 1.3.0 + * @example + * expect(result).toBeDefined(); + */ + function toBeDefined() { + return { + compare: function(actual) { + return { + pass: void 0 !== actual + }; + } + }; + } + + return toBeDefined; +}; + +getJasmineRequireObj().toBeFalse = function() { + /** + * {@link expect} the actual value to be `false`. + * @function + * @name matchers#toBeFalse + * @since 3.5.0 + * @example + * expect(result).toBeFalse(); + */ + function toBeFalse() { + return { + compare: function(actual) { + return { + pass: actual === false + }; + } + }; + } + + return toBeFalse; +}; + +getJasmineRequireObj().toBeFalsy = function() { + /** + * {@link expect} the actual value to be falsy + * @function + * @name matchers#toBeFalsy + * @since 2.0.0 + * @example + * expect(result).toBeFalsy(); + */ + function toBeFalsy() { + return { + compare: function(actual) { + return { + pass: !actual + }; + } + }; + } + + return toBeFalsy; +}; + +getJasmineRequireObj().toBeGreaterThan = function() { + /** + * {@link expect} the actual value to be greater than the expected value. + * @function + * @name matchers#toBeGreaterThan + * @since 2.0.0 + * @param {Number} expected - The value to compare against. + * @example + * expect(result).toBeGreaterThan(3); + */ + function toBeGreaterThan() { + return { + compare: function(actual, expected) { + return { + pass: actual > expected + }; + } + }; + } + + return toBeGreaterThan; +}; + +getJasmineRequireObj().toBeGreaterThanOrEqual = function() { + /** + * {@link expect} the actual value to be greater than or equal to the expected value. + * @function + * @name matchers#toBeGreaterThanOrEqual + * @since 2.0.0 + * @param {Number} expected - The expected value to compare against. + * @example + * expect(result).toBeGreaterThanOrEqual(25); + */ + function toBeGreaterThanOrEqual() { + return { + compare: function(actual, expected) { + return { + pass: actual >= expected + }; + } + }; + } + + return toBeGreaterThanOrEqual; +}; + +getJasmineRequireObj().toBeInstanceOf = function(j$) { + const usageError = j$.formatErrorMsg( + '', + 'expect(value).toBeInstanceOf()' + ); + + /** + * {@link expect} the actual to be an instance of the expected class + * @function + * @name matchers#toBeInstanceOf + * @since 3.5.0 + * @param {Object} expected - The class or constructor function to check for + * @example + * expect('foo').toBeInstanceOf(String); + * expect(3).toBeInstanceOf(Number); + * expect(new Error()).toBeInstanceOf(Error); + */ + function toBeInstanceOf(matchersUtil) { + return { + compare: function(actual, expected) { + const actualType = + actual && actual.constructor + ? j$.fnNameFor(actual.constructor) + : matchersUtil.pp(actual); + const expectedType = expected + ? j$.fnNameFor(expected) + : matchersUtil.pp(expected); + let expectedMatcher; + let pass; + + try { + expectedMatcher = new j$.Any(expected); + pass = expectedMatcher.asymmetricMatch(actual); + } catch (error) { + throw new Error( + usageError('Expected value is not a constructor function') + ); + } + + if (pass) { + return { + pass: true, + message: + 'Expected instance of ' + + actualType + + ' not to be an instance of ' + + expectedType + }; + } else { + return { + pass: false, + message: + 'Expected instance of ' + + actualType + + ' to be an instance of ' + + expectedType + }; + } + } + }; + } + + return toBeInstanceOf; +}; + +getJasmineRequireObj().toBeLessThan = function() { + /** + * {@link expect} the actual value to be less than the expected value. + * @function + * @name matchers#toBeLessThan + * @since 2.0.0 + * @param {Number} expected - The expected value to compare against. + * @example + * expect(result).toBeLessThan(0); + */ + function toBeLessThan() { + return { + compare: function(actual, expected) { + return { + pass: actual < expected + }; + } + }; + } + + return toBeLessThan; +}; + +getJasmineRequireObj().toBeLessThanOrEqual = function() { + /** + * {@link expect} the actual value to be less than or equal to the expected value. + * @function + * @name matchers#toBeLessThanOrEqual + * @since 2.0.0 + * @param {Number} expected - The expected value to compare against. + * @example + * expect(result).toBeLessThanOrEqual(123); + */ + function toBeLessThanOrEqual() { + return { + compare: function(actual, expected) { + return { + pass: actual <= expected + }; + } + }; + } + + return toBeLessThanOrEqual; +}; + +getJasmineRequireObj().toBeNaN = function(j$) { + /** + * {@link expect} the actual value to be `NaN` (Not a Number). + * @function + * @name matchers#toBeNaN + * @since 1.3.0 + * @example + * expect(thing).toBeNaN(); + */ + function toBeNaN(matchersUtil) { + return { + compare: function(actual) { + const result = { + pass: actual !== actual + }; + + if (result.pass) { + result.message = 'Expected actual not to be NaN.'; + } else { + result.message = function() { + return 'Expected ' + matchersUtil.pp(actual) + ' to be NaN.'; + }; + } + + return result; + } + }; + } + + return toBeNaN; +}; + +getJasmineRequireObj().toBeNegativeInfinity = function(j$) { + /** + * {@link expect} the actual value to be `-Infinity` (-infinity). + * @function + * @name matchers#toBeNegativeInfinity + * @since 2.6.0 + * @example + * expect(thing).toBeNegativeInfinity(); + */ + function toBeNegativeInfinity(matchersUtil) { + return { + compare: function(actual) { + const result = { + pass: actual === Number.NEGATIVE_INFINITY + }; + + if (result.pass) { + result.message = 'Expected actual not to be -Infinity.'; + } else { + result.message = function() { + return 'Expected ' + matchersUtil.pp(actual) + ' to be -Infinity.'; + }; + } + + return result; + } + }; + } + + return toBeNegativeInfinity; +}; + +getJasmineRequireObj().toBeNull = function() { + /** + * {@link expect} the actual value to be `null`. + * @function + * @name matchers#toBeNull + * @since 1.3.0 + * @example + * expect(result).toBeNull(); + */ + function toBeNull() { + return { + compare: function(actual) { + return { + pass: actual === null + }; + } + }; + } + + return toBeNull; +}; + +getJasmineRequireObj().toBePositiveInfinity = function(j$) { + /** + * {@link expect} the actual value to be `Infinity` (infinity). + * @function + * @name matchers#toBePositiveInfinity + * @since 2.6.0 + * @example + * expect(thing).toBePositiveInfinity(); + */ + function toBePositiveInfinity(matchersUtil) { + return { + compare: function(actual) { + const result = { + pass: actual === Number.POSITIVE_INFINITY + }; + + if (result.pass) { + result.message = 'Expected actual not to be Infinity.'; + } else { + result.message = function() { + return 'Expected ' + matchersUtil.pp(actual) + ' to be Infinity.'; + }; + } + + return result; + } + }; + } + + return toBePositiveInfinity; +}; + +getJasmineRequireObj().toBeTrue = function() { + /** + * {@link expect} the actual value to be `true`. + * @function + * @name matchers#toBeTrue + * @since 3.5.0 + * @example + * expect(result).toBeTrue(); + */ + function toBeTrue() { + return { + compare: function(actual) { + return { + pass: actual === true + }; + } + }; + } + + return toBeTrue; +}; + +getJasmineRequireObj().toBeTruthy = function() { + /** + * {@link expect} the actual value to be truthy. + * @function + * @name matchers#toBeTruthy + * @since 2.0.0 + * @example + * expect(thing).toBeTruthy(); + */ + function toBeTruthy() { + return { + compare: function(actual) { + return { + pass: !!actual + }; + } + }; + } + + return toBeTruthy; +}; + +getJasmineRequireObj().toBeUndefined = function() { + /** + * {@link expect} the actual value to be `undefined`. + * @function + * @name matchers#toBeUndefined + * @since 1.3.0 + * @example + * expect(result).toBeUndefined(): + */ + function toBeUndefined() { + return { + compare: function(actual) { + return { + pass: void 0 === actual + }; + } + }; + } + + return toBeUndefined; +}; + +getJasmineRequireObj().toContain = function() { + /** + * {@link expect} the actual value to contain a specific value. + * @function + * @name matchers#toContain + * @since 2.0.0 + * @param {Object} expected - The value to look for. + * @example + * expect(array).toContain(anElement); + * expect(string).toContain(substring); + */ + function toContain(matchersUtil) { + return { + compare: function(actual, expected) { + return { + pass: matchersUtil.contains(actual, expected) + }; + } + }; + } + + return toContain; +}; + +getJasmineRequireObj().toEqual = function(j$) { + /** + * {@link expect} the actual value to be equal to the expected, using deep equality comparison. + * @function + * @name matchers#toEqual + * @since 1.3.0 + * @param {Object} expected - Expected value + * @example + * expect(bigObject).toEqual({"foo": ['bar', 'baz']}); + */ + function toEqual(matchersUtil) { + return { + compare: function(actual, expected) { + const result = { + pass: false + }, + diffBuilder = new j$.DiffBuilder({ prettyPrinter: matchersUtil.pp }); + + result.pass = matchersUtil.equals(actual, expected, diffBuilder); + + // TODO: only set error message if test fails + result.message = diffBuilder.getMessage(); + + return result; + } + }; + } + + return toEqual; +}; + +getJasmineRequireObj().toHaveBeenCalled = function(j$) { + const getErrorMsg = j$.formatErrorMsg( + '', + 'expect().toHaveBeenCalled()' + ); + + /** + * {@link expect} the actual (a {@link Spy}) to have been called. + * @function + * @name matchers#toHaveBeenCalled + * @since 1.3.0 + * @example + * expect(mySpy).toHaveBeenCalled(); + * expect(mySpy).not.toHaveBeenCalled(); + */ + function toHaveBeenCalled(matchersUtil) { + return { + compare: function(actual) { + const result = {}; + + if (!j$.isSpy(actual)) { + throw new Error( + getErrorMsg( + 'Expected a spy, but got ' + matchersUtil.pp(actual) + '.' + ) + ); + } + + if (arguments.length > 1) { + throw new Error( + getErrorMsg('Does not take arguments, use toHaveBeenCalledWith') + ); + } + + result.pass = actual.calls.any(); + + result.message = result.pass + ? 'Expected spy ' + actual.and.identity + ' not to have been called.' + : 'Expected spy ' + actual.and.identity + ' to have been called.'; + + return result; + } + }; + } + + return toHaveBeenCalled; +}; + +getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) { + const getErrorMsg = j$.formatErrorMsg( + '', + 'expect().toHaveBeenCalledBefore()' + ); + + /** + * {@link expect} the actual value (a {@link Spy}) to have been called before another {@link Spy}. + * @function + * @name matchers#toHaveBeenCalledBefore + * @since 2.6.0 + * @param {Spy} expected - {@link Spy} that should have been called after the `actual` {@link Spy}. + * @example + * expect(mySpy).toHaveBeenCalledBefore(otherSpy); + */ + function toHaveBeenCalledBefore(matchersUtil) { + return { + compare: function(firstSpy, latterSpy) { + if (!j$.isSpy(firstSpy)) { + throw new Error( + getErrorMsg( + 'Expected a spy, but got ' + matchersUtil.pp(firstSpy) + '.' + ) + ); + } + if (!j$.isSpy(latterSpy)) { + throw new Error( + getErrorMsg( + 'Expected a spy, but got ' + matchersUtil.pp(latterSpy) + '.' + ) + ); + } + + const result = { pass: false }; + + if (!firstSpy.calls.count()) { + result.message = + 'Expected spy ' + firstSpy.and.identity + ' to have been called.'; + return result; + } + if (!latterSpy.calls.count()) { + result.message = + 'Expected spy ' + latterSpy.and.identity + ' to have been called.'; + return result; + } + + const latest1stSpyCall = firstSpy.calls.mostRecent().invocationOrder; + const first2ndSpyCall = latterSpy.calls.first().invocationOrder; + + result.pass = latest1stSpyCall < first2ndSpyCall; + + if (result.pass) { + result.message = + 'Expected spy ' + + firstSpy.and.identity + + ' to not have been called before spy ' + + latterSpy.and.identity + + ', but it was'; + } else { + const first1stSpyCall = firstSpy.calls.first().invocationOrder; + const latest2ndSpyCall = latterSpy.calls.mostRecent().invocationOrder; + + if (first1stSpyCall < first2ndSpyCall) { + result.message = + 'Expected latest call to spy ' + + firstSpy.and.identity + + ' to have been called before first call to spy ' + + latterSpy.and.identity + + ' (no interleaved calls)'; + } else if (latest2ndSpyCall > latest1stSpyCall) { + result.message = + 'Expected first call to spy ' + + latterSpy.and.identity + + ' to have been called after latest call to spy ' + + firstSpy.and.identity + + ' (no interleaved calls)'; + } else { + result.message = + 'Expected spy ' + + firstSpy.and.identity + + ' to have been called before spy ' + + latterSpy.and.identity; + } + } + + return result; + } + }; + } + + return toHaveBeenCalledBefore; +}; + +getJasmineRequireObj().toHaveBeenCalledOnceWith = function(j$) { + const getErrorMsg = j$.formatErrorMsg( + '', + 'expect().toHaveBeenCalledOnceWith(...arguments)' + ); + + /** + * {@link expect} the actual (a {@link Spy}) to have been called exactly once, and exactly with the particular arguments. + * @function + * @name matchers#toHaveBeenCalledOnceWith + * @since 3.6.0 + * @param {...Object} - The arguments to look for + * @example + * expect(mySpy).toHaveBeenCalledOnceWith('foo', 'bar', 2); + */ + function toHaveBeenCalledOnceWith(util) { + return { + compare: function() { + const args = Array.prototype.slice.call(arguments, 0), + actual = args[0], + expectedArgs = args.slice(1); + + if (!j$.isSpy(actual)) { + throw new Error( + getErrorMsg('Expected a spy, but got ' + util.pp(actual) + '.') + ); + } + + const prettyPrintedCalls = actual.calls + .allArgs() + .map(function(argsForCall) { + return ' ' + util.pp(argsForCall); + }); + + if ( + actual.calls.count() === 1 && + util.contains(actual.calls.allArgs(), expectedArgs) + ) { + return { + pass: true, + message: + 'Expected spy ' + + actual.and.identity + + ' to have been called 0 times, multiple times, or once, but with arguments different from:\n' + + ' ' + + util.pp(expectedArgs) + + '\n' + + 'But the actual call was:\n' + + prettyPrintedCalls.join(',\n') + + '.\n\n' + }; + } + + function getDiffs() { + return actual.calls.allArgs().map(function(argsForCall, callIx) { + const diffBuilder = new j$.DiffBuilder(); + util.equals(argsForCall, expectedArgs, diffBuilder); + return diffBuilder.getMessage(); + }); + } + + function butString() { + switch (actual.calls.count()) { + case 0: + return 'But it was never called.\n\n'; + case 1: + return ( + 'But the actual call was:\n' + + prettyPrintedCalls.join(',\n') + + '.\n' + + getDiffs().join('\n') + + '\n\n' + ); + default: + return ( + 'But the actual calls were:\n' + + prettyPrintedCalls.join(',\n') + + '.\n\n' + ); + } + } + + return { + pass: false, + message: + 'Expected spy ' + + actual.and.identity + + ' to have been called only once, and with given args:\n' + + ' ' + + util.pp(expectedArgs) + + '\n' + + butString() + }; + } + }; + } + + return toHaveBeenCalledOnceWith; +}; + +getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { + const getErrorMsg = j$.formatErrorMsg( + '', + 'expect().toHaveBeenCalledTimes()' + ); + + /** + * {@link expect} the actual (a {@link Spy}) to have been called the specified number of times. + * @function + * @name matchers#toHaveBeenCalledTimes + * @since 2.4.0 + * @param {Number} expected - The number of invocations to look for. + * @example + * expect(mySpy).toHaveBeenCalledTimes(3); + */ + function toHaveBeenCalledTimes(matchersUtil) { + return { + compare: function(actual, expected) { + if (!j$.isSpy(actual)) { + throw new Error( + getErrorMsg( + 'Expected a spy, but got ' + matchersUtil.pp(actual) + '.' + ) + ); + } + + const args = Array.prototype.slice.call(arguments, 0), + result = { pass: false }; + + if (!j$.isNumber_(expected)) { + throw new Error( + getErrorMsg( + 'The expected times failed is a required argument and must be a number.' + ) + ); + } + + actual = args[0]; + const calls = actual.calls.count(); + const timesMessage = expected === 1 ? 'once' : expected + ' times'; + result.pass = calls === expected; + result.message = result.pass + ? 'Expected spy ' + + actual.and.identity + + ' not to have been called ' + + timesMessage + + '. It was called ' + + calls + + ' times.' + : 'Expected spy ' + + actual.and.identity + + ' to have been called ' + + timesMessage + + '. It was called ' + + calls + + ' times.'; + return result; + } + }; + } + + return toHaveBeenCalledTimes; +}; + +getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { + const getErrorMsg = j$.formatErrorMsg( + '', + 'expect().toHaveBeenCalledWith(...arguments)' + ); + + /** + * {@link expect} the actual (a {@link Spy}) to have been called with particular arguments at least once. + * @function + * @name matchers#toHaveBeenCalledWith + * @since 1.3.0 + * @param {...Object} - The arguments to look for + * @example + * expect(mySpy).toHaveBeenCalledWith('foo', 'bar', 2); + */ + function toHaveBeenCalledWith(matchersUtil) { + return { + compare: function() { + const args = Array.prototype.slice.call(arguments, 0), + actual = args[0], + expectedArgs = args.slice(1), + result = { pass: false }; + + if (!j$.isSpy(actual)) { + throw new Error( + getErrorMsg( + 'Expected a spy, but got ' + matchersUtil.pp(actual) + '.' + ) + ); + } + + if (!actual.calls.any()) { + result.message = function() { + return ( + 'Expected spy ' + + actual.and.identity + + ' to have been called with:\n' + + ' ' + + matchersUtil.pp(expectedArgs) + + '\nbut it was never called.' + ); + }; + return result; + } + + if (matchersUtil.contains(actual.calls.allArgs(), expectedArgs)) { + result.pass = true; + result.message = function() { + return ( + 'Expected spy ' + + actual.and.identity + + ' not to have been called with:\n' + + ' ' + + matchersUtil.pp(expectedArgs) + + '\nbut it was.' + ); + }; + } else { + result.message = function() { + const prettyPrintedCalls = actual.calls + .allArgs() + .map(function(argsForCall) { + return ' ' + matchersUtil.pp(argsForCall); + }); + + const diffs = actual.calls + .allArgs() + .map(function(argsForCall, callIx) { + const diffBuilder = new j$.DiffBuilder(); + matchersUtil.equals(argsForCall, expectedArgs, diffBuilder); + return ( + 'Call ' + + callIx + + ':\n' + + diffBuilder.getMessage().replace(/^/gm, ' ') + ); + }); + + return ( + 'Expected spy ' + + actual.and.identity + + ' to have been called with:\n' + + ' ' + + matchersUtil.pp(expectedArgs) + + '\n' + + '' + + 'but actual calls were:\n' + + prettyPrintedCalls.join(',\n') + + '.\n\n' + + diffs.join('\n') + ); + }; + } + + return result; + } + }; + } + + return toHaveBeenCalledWith; +}; + +getJasmineRequireObj().toHaveClass = function(j$) { + /** + * {@link expect} the actual value to be a DOM element that has the expected class + * @function + * @name matchers#toHaveClass + * @since 3.0.0 + * @param {Object} expected - The class name to test for + * @example + * const el = document.createElement('div'); + * el.className = 'foo bar baz'; + * expect(el).toHaveClass('bar'); + */ + function toHaveClass(matchersUtil) { + return { + compare: function(actual, expected) { + if (!isElement(actual)) { + throw new Error(matchersUtil.pp(actual) + ' is not a DOM element'); + } + + return { + pass: actual.classList.contains(expected) + }; + } + }; + } + + function isElement(maybeEl) { + return ( + maybeEl && maybeEl.classList && j$.isFunction_(maybeEl.classList.contains) + ); + } + + return toHaveClass; +}; + +getJasmineRequireObj().toHaveSize = function(j$) { + /** + * {@link expect} the actual size to be equal to the expected, using array-like length or object keys size. + * @function + * @name matchers#toHaveSize + * @since 3.6.0 + * @param {Object} expected - Expected size + * @example + * array = [1,2]; + * expect(array).toHaveSize(2); + */ + function toHaveSize() { + return { + compare: function(actual, expected) { + const result = { + pass: false + }; + + if ( + j$.isA_('WeakSet', actual) || + j$.isWeakMap(actual) || + j$.isDataView(actual) + ) { + throw new Error('Cannot get size of ' + actual + '.'); + } + + if (j$.isSet(actual) || j$.isMap(actual)) { + result.pass = actual.size === expected; + } else if (isLength(actual.length)) { + result.pass = actual.length === expected; + } else { + result.pass = Object.keys(actual).length === expected; + } + + return result; + } + }; + } + + const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; + function isLength(value) { + return ( + typeof value == 'number' && + value > -1 && + value % 1 === 0 && + value <= MAX_SAFE_INTEGER + ); + } + + return toHaveSize; +}; + +getJasmineRequireObj().toHaveSpyInteractions = function(j$) { + const getErrorMsg = j$.formatErrorMsg( + '', + 'expect().toHaveSpyInteractions()' + ); + + /** + * {@link expect} the actual (a {@link SpyObj}) spies to have been called. + * @function + * @name matchers#toHaveSpyInteractions + * @since 4.1.0 + * @example + * expect(mySpyObj).toHaveSpyInteractions(); + * expect(mySpyObj).not.toHaveSpyInteractions(); + */ + function toHaveSpyInteractions(matchersUtil) { + return { + compare: function(actual) { + const result = {}; + + if (!j$.isObject_(actual)) { + throw new Error( + getErrorMsg('Expected a spy object, but got ' + typeof actual + '.') + ); + } + + if (arguments.length > 1) { + throw new Error(getErrorMsg('Does not take arguments')); + } + + result.pass = false; + let hasSpy = false; + const calledSpies = []; + for (const spy of Object.values(actual)) { + if (!j$.isSpy(spy)) continue; + hasSpy = true; + + if (spy.calls.any()) { + result.pass = true; + calledSpies.push([spy.and.identity, spy.calls.count()]); + } + } + + if (!hasSpy) { + throw new Error( + getErrorMsg( + 'Expected a spy object with spies, but object has no spies.' + ) + ); + } + + let resultMessage; + if (result.pass) { + resultMessage = + 'Expected spy object spies not to have been called, ' + + 'but the following spies were called: '; + resultMessage += calledSpies + .map(([spyName, spyCount]) => { + return `${spyName} called ${spyCount} time(s)`; + }) + .join(', '); + } else { + resultMessage = + 'Expected spy object spies to have been called, ' + + 'but no spies were called.'; + } + result.message = resultMessage; + + return result; + } + }; + } + + return toHaveSpyInteractions; +}; + +getJasmineRequireObj().toMatch = function(j$) { + const getErrorMsg = j$.formatErrorMsg( + '', + 'expect().toMatch( || )' + ); + + /** + * {@link expect} the actual value to match a regular expression + * @function + * @name matchers#toMatch + * @since 1.3.0 + * @param {RegExp|String} expected - Value to look for in the string. + * @example + * expect("my string").toMatch(/string$/); + * expect("other string").toMatch("her"); + */ + function toMatch() { + return { + compare: function(actual, expected) { + if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { + throw new Error(getErrorMsg('Expected is not a String or a RegExp')); + } + + const regexp = new RegExp(expected); + + return { + pass: regexp.test(actual) + }; + } + }; + } + + return toMatch; +}; + +getJasmineRequireObj().toThrow = function(j$) { + const getErrorMsg = j$.formatErrorMsg( + '', + 'expect(function() {}).toThrow()' + ); + + /** + * {@link expect} a function to `throw` something. + * @function + * @name matchers#toThrow + * @since 2.0.0 + * @param {Object} [expected] - Value that should be thrown. If not provided, simply the fact that something was thrown will be checked. + * @example + * expect(function() { return 'things'; }).toThrow('foo'); + * expect(function() { return 'stuff'; }).toThrow(); + */ + function toThrow(matchersUtil) { + return { + compare: function(actual, expected) { + const result = { pass: false }; + let threw = false; + let thrown; + + if (typeof actual != 'function') { + throw new Error(getErrorMsg('Actual is not a Function')); + } + + try { + actual(); + } catch (e) { + threw = true; + thrown = e; + } + + if (!threw) { + result.message = 'Expected function to throw an exception.'; + return result; + } + + if (arguments.length == 1) { + result.pass = true; + result.message = function() { + return ( + 'Expected function not to throw, but it threw ' + + matchersUtil.pp(thrown) + + '.' + ); + }; + + return result; + } + + if (matchersUtil.equals(thrown, expected)) { + result.pass = true; + result.message = function() { + return ( + 'Expected function not to throw ' + + matchersUtil.pp(expected) + + '.' + ); + }; + } else { + result.message = function() { + return ( + 'Expected function to throw ' + + matchersUtil.pp(expected) + + ', but it threw ' + + matchersUtil.pp(thrown) + + '.' + ); + }; + } + + return result; + } + }; + } + + return toThrow; +}; + +getJasmineRequireObj().toThrowError = function(j$) { + const getErrorMsg = j$.formatErrorMsg( + '', + 'expect(function() {}).toThrowError(, )' + ); + + /** + * {@link expect} a function to `throw` an `Error`. + * @function + * @name matchers#toThrowError + * @since 2.0.0 + * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used. + * @param {RegExp|String} [message] - The message that should be set on the thrown `Error` + * @example + * expect(function() { return 'things'; }).toThrowError(MyCustomError, 'message'); + * expect(function() { return 'things'; }).toThrowError(MyCustomError, /bar/); + * expect(function() { return 'stuff'; }).toThrowError(MyCustomError); + * expect(function() { return 'other'; }).toThrowError(/foo/); + * expect(function() { return 'other'; }).toThrowError(); + */ + function toThrowError(matchersUtil) { + return { + compare: function(actual) { + const errorMatcher = getMatcher.apply(null, arguments); + + if (typeof actual != 'function') { + throw new Error(getErrorMsg('Actual is not a Function')); + } + + let thrown; + + try { + actual(); + return fail('Expected function to throw an Error.'); + } catch (e) { + thrown = e; + } + + if (!j$.isError_(thrown)) { + return fail(function() { + return ( + 'Expected function to throw an Error, but it threw ' + + matchersUtil.pp(thrown) + + '.' + ); + }); + } + + return errorMatcher.match(thrown); + } + }; + + function getMatcher() { + let expected, errorType; + + if (arguments[2]) { + errorType = arguments[1]; + expected = arguments[2]; + if (!isAnErrorType(errorType)) { + throw new Error(getErrorMsg('Expected error type is not an Error.')); + } + + return exactMatcher(expected, errorType); + } else if (arguments[1]) { + expected = arguments[1]; + + if (isAnErrorType(arguments[1])) { + return exactMatcher(null, arguments[1]); + } else { + return exactMatcher(arguments[1], null); + } + } else { + return anyMatcher(); + } + } + + function anyMatcher() { + return { + match: function(error) { + return pass( + 'Expected function not to throw an Error, but it threw ' + + j$.fnNameFor(error) + + '.' + ); + } + }; + } + + function exactMatcher(expected, errorType) { + if (expected && !isStringOrRegExp(expected)) { + if (errorType) { + throw new Error( + getErrorMsg('Expected error message is not a string or RegExp.') + ); + } else { + throw new Error( + getErrorMsg('Expected is not an Error, string, or RegExp.') + ); + } + } + + function messageMatch(message) { + if (typeof expected == 'string') { + return expected == message; + } else { + return expected.test(message); + } + } + + const errorTypeDescription = errorType + ? j$.fnNameFor(errorType) + : 'an exception'; + + function thrownDescription(thrown) { + const thrownName = errorType + ? j$.fnNameFor(thrown.constructor) + : 'an exception'; + let thrownMessage = ''; + + if (expected) { + thrownMessage = ' with message ' + matchersUtil.pp(thrown.message); + } + + return thrownName + thrownMessage; + } + + function messageDescription() { + if (expected === null) { + return ''; + } else if (expected instanceof RegExp) { + return ' with a message matching ' + matchersUtil.pp(expected); + } else { + return ' with message ' + matchersUtil.pp(expected); + } + } + + function matches(error) { + return ( + (errorType === null || error instanceof errorType) && + (expected === null || messageMatch(error.message)) + ); + } + + return { + match: function(thrown) { + if (matches(thrown)) { + return pass(function() { + return ( + 'Expected function not to throw ' + + errorTypeDescription + + messageDescription() + + '.' + ); + }); + } else { + return fail(function() { + return ( + 'Expected function to throw ' + + errorTypeDescription + + messageDescription() + + ', but it threw ' + + thrownDescription(thrown) + + '.' + ); + }); + } + } + }; + } + + function isStringOrRegExp(potential) { + return potential instanceof RegExp || typeof potential == 'string'; + } + + function isAnErrorType(type) { + if (typeof type !== 'function') { + return false; + } + + const Surrogate = function() {}; + Surrogate.prototype = type.prototype; + return j$.isError_(new Surrogate()); + } + } + + function pass(message) { + return { + pass: true, + message: message + }; + } + + function fail(message) { + return { + pass: false, + message: message + }; + } + + return toThrowError; +}; + +getJasmineRequireObj().toThrowMatching = function(j$) { + const usageError = j$.formatErrorMsg( + '', + 'expect(function() {}).toThrowMatching()' + ); + + /** + * {@link expect} a function to `throw` something matching a predicate. + * @function + * @name matchers#toThrowMatching + * @since 3.0.0 + * @param {Function} predicate - A function that takes the thrown exception as its parameter and returns true if it matches. + * @example + * expect(function() { throw new Error('nope'); }).toThrowMatching(function(thrown) { return thrown.message === 'nope'; }); + */ + function toThrowMatching(matchersUtil) { + return { + compare: function(actual, predicate) { + if (typeof actual !== 'function') { + throw new Error(usageError('Actual is not a Function')); + } + + if (typeof predicate !== 'function') { + throw new Error(usageError('Predicate is not a Function')); + } + + let thrown; + + try { + actual(); + return fail('Expected function to throw an exception.'); + } catch (e) { + thrown = e; + } + + if (predicate(thrown)) { + return pass( + 'Expected function not to throw an exception matching a predicate.' + ); + } else { + return fail(function() { + return ( + 'Expected function to throw an exception matching a predicate, ' + + 'but it threw ' + + thrownDescription(thrown) + + '.' + ); + }); + } + } + }; + + function thrownDescription(thrown) { + if (thrown && thrown.constructor) { + return ( + j$.fnNameFor(thrown.constructor) + + ' with message ' + + matchersUtil.pp(thrown.message) + ); + } else { + return matchersUtil.pp(thrown); + } + } + } + + function pass(message) { + return { + pass: true, + message: message + }; + } + + function fail(message) { + return { + pass: false, + message: message + }; + } + + return toThrowMatching; +}; + +getJasmineRequireObj().MockDate = function(j$) { + function MockDate(global) { + let currentTime = 0; + + if (!global || !global.Date) { + this.install = function() {}; + this.tick = function() {}; + this.uninstall = function() {}; + return this; + } + + const GlobalDate = global.Date; + + this.install = function(mockDate) { + if (mockDate instanceof GlobalDate) { + currentTime = mockDate.getTime(); + } else { + if (!j$.util.isUndefined(mockDate)) { + throw new Error( + 'The argument to jasmine.clock().mockDate(), if specified, ' + + 'should be a Date instance.' + ); + } + + currentTime = new GlobalDate().getTime(); + } + + global.Date = FakeDate; + }; + + this.tick = function(millis) { + millis = millis || 0; + currentTime = currentTime + millis; + }; + + this.uninstall = function() { + currentTime = 0; + global.Date = GlobalDate; + }; + + createDateProperties(); + + return this; + + function FakeDate() { + switch (arguments.length) { + case 0: + return new GlobalDate(currentTime); + case 1: + return new GlobalDate(arguments[0]); + case 2: + return new GlobalDate(arguments[0], arguments[1]); + case 3: + return new GlobalDate(arguments[0], arguments[1], arguments[2]); + case 4: + return new GlobalDate( + arguments[0], + arguments[1], + arguments[2], + arguments[3] + ); + case 5: + return new GlobalDate( + arguments[0], + arguments[1], + arguments[2], + arguments[3], + arguments[4] + ); + case 6: + return new GlobalDate( + arguments[0], + arguments[1], + arguments[2], + arguments[3], + arguments[4], + arguments[5] + ); + default: + return new GlobalDate( + arguments[0], + arguments[1], + arguments[2], + arguments[3], + arguments[4], + arguments[5], + arguments[6] + ); + } + } + + function createDateProperties() { + FakeDate.prototype = GlobalDate.prototype; + + FakeDate.now = function() { + return currentTime; + }; + + FakeDate.toSource = GlobalDate.toSource; + FakeDate.toString = GlobalDate.toString; + FakeDate.parse = GlobalDate.parse; + FakeDate.UTC = GlobalDate.UTC; + } + } + + return MockDate; +}; + +getJasmineRequireObj().NeverSkipPolicy = function(j$) { + function NeverSkipPolicy(queueableFns) {} + + NeverSkipPolicy.prototype.skipTo = function(lastRanFnIx) { + return lastRanFnIx + 1; + }; + + NeverSkipPolicy.prototype.fnErrored = function(fnIx) {}; + + return NeverSkipPolicy; +}; + +getJasmineRequireObj().ParallelReportDispatcher = function(j$) { + 'use strict'; + + /** + * @class ParallelReportDispatcher + * @implements Reporter + * @classdesc A report dispatcher packaged for convenient use from outside jasmine-core. + * + * This is intended to help packages like `jasmine` (the Jasmine runner for + * Node.js) do their own report dispatching in order to support parallel + * execution. If you aren't implementing a runner package that supports + * parallel execution, this class probably isn't what you're looking for. + * + * Warning: Do not use ParallelReportDispatcher in the same process that + * Jasmine specs run in. Doing so will break Jasmine's error handling. + * @param onError {function} Function called when an unhandled exception, unhandled promise rejection, or explicit reporter failure occurs + */ + function ParallelReportDispatcher(onError, deps = {}) { + const ReportDispatcher = deps.ReportDispatcher || j$.ReportDispatcher; + const QueueRunner = deps.QueueRunner || j$.QueueRunner; + const globalErrors = deps.globalErrors || new j$.GlobalErrors(); + const dispatcher = new ReportDispatcher( + j$.reporterEvents, + function(queueRunnerOptions) { + queueRunnerOptions = { + ...queueRunnerOptions, + globalErrors, + timeout: { setTimeout, clearTimeout }, + fail: function(error) { + // A callback-style async reporter called either done.fail() + // or done(anError). + if (!error) { + error = new Error('A reporter called done.fail()'); + } + + onError(error); + }, + onException: function(error) { + // A reporter method threw an exception or returned a rejected + // promise, or there was an unhandled exception or unhandled promise + // rejection while an asynchronous reporter method was running. + onError(error); + } + }; + new QueueRunner(queueRunnerOptions).execute(); + }, + function(error) { + // A reporter called done() more than once. + onError(error); + } + ); + + const self = { + /** + * Adds a reporter to the list of reporters that events will be dispatched to. + * @function + * @name ParallelReportDispatcher#addReporter + * @param {Reporter} reporterToAdd The reporter to be added. + * @see custom_reporter + */ + addReporter: dispatcher.addReporter.bind(dispatcher), + /** + * Clears all registered reporters. + * @function + * @name ParallelReportDispatcher#clearReporters + */ + clearReporters: dispatcher.clearReporters.bind(dispatcher), + /** + * Installs a global error handler. After this method is called, any + * unhandled exceptions or unhandled promise rejections will be passed to + * the onError callback that was passed to the constructor. + * @function + * @name ParallelReportDispatcher#installGlobalErrors + */ + installGlobalErrors: globalErrors.install.bind(globalErrors), + /** + * Uninstalls the global error handler. + * @function + * @name ParallelReportDispatcher#uninstallGlobalErrors + */ + uninstallGlobalErrors: function() { + // late-bind uninstall because it doesn't exist until install is called + globalErrors.uninstall(globalErrors); + } + }; + + for (const eventName of j$.reporterEvents) { + self[eventName] = dispatcher[eventName].bind(dispatcher); + } + + return self; + } + + return ParallelReportDispatcher; +}; + +getJasmineRequireObj().makePrettyPrinter = function(j$) { + class SinglePrettyPrintRun { + constructor(customObjectFormatters, pp) { + this.customObjectFormatters_ = customObjectFormatters; + this.ppNestLevel_ = 0; + this.seen = []; + this.length = 0; + this.stringParts = []; + this.pp_ = pp; + } + + format(value) { + this.ppNestLevel_++; + try { + const customFormatResult = this.applyCustomFormatters_(value); + + if (customFormatResult) { + this.emitScalar(customFormatResult); + } else if (j$.util.isUndefined(value)) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === 0 && 1 / value === -Infinity) { + this.emitScalar('-0'); + } else if (value === j$.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString(this.pp_)); + } else if (j$.isString_(value)) { + this.emitString(value); + } else if (j$.isSpy(value)) { + this.emitScalar('spy on ' + value.and.identity); + } else if (j$.isSpy(value.toString)) { + this.emitScalar('spy on ' + value.toString.and.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (j$.isDomNode(value)) { + if (value.tagName) { + this.emitDomElement(value); + } else { + this.emitScalar('HTMLNode'); + } + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (j$.isSet(value)) { + this.emitSet(value); + } else if (j$.isMap(value)) { + this.emitMap(value); + } else if (j$.isTypedArray_(value)) { + this.emitTypedArray(value); + } else if ( + value.toString && + typeof value === 'object' && + !j$.isArray_(value) && + hasCustomToString(value) + ) { + try { + this.emitScalar(value.toString()); + } catch (e) { + this.emitScalar('has-invalid-toString-method'); + } + } else if (this.seen.includes(value)) { + this.emitScalar( + '' + ); + } else if (j$.isArray_(value) || j$.isA_('Object', value)) { + this.seen.push(value); + if (j$.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + this.seen.pop(); + } else { + this.emitScalar(value.toString()); + } + } catch (e) { + if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { + throw e; + } + } finally { + this.ppNestLevel_--; + } + } + + applyCustomFormatters_(value) { + return customFormat(value, this.customObjectFormatters_); + } + + iterateObject(obj, fn) { + const objKeys = j$.MatchersUtil.keys(obj, j$.isArray_(obj)); + const length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + + for (let i = 0; i < length; i++) { + fn(objKeys[i]); + } + + return objKeys.length > length; + } + + emitScalar(value) { + this.append(value); + } + + emitString(value) { + this.append("'" + value + "'"); + } + + emitArray(array) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Array'); + return; + } + + const length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + this.append('[ '); + + for (let i = 0; i < length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + if (array.length > length) { + this.append(', ...'); + } + + let first = array.length === 0; + const wasTruncated = this.iterateObject(array, property => { + if (first) { + first = false; + } else { + this.append(', '); + } + + this.formatProperty(array, property); + }); + + if (wasTruncated) { + this.append(', ...'); + } + + this.append(' ]'); + } + + emitSet(set) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Set'); + return; + } + this.append('Set( '); + const size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + let i = 0; + set.forEach(function(value, key) { + if (i >= size) { + return; + } + if (i > 0) { + this.append(', '); + } + this.format(value); + + i++; + }, this); + if (set.size > size) { + this.append(', ...'); + } + this.append(' )'); + } + + emitMap(map) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Map'); + return; + } + this.append('Map( '); + const size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + let i = 0; + map.forEach(function(value, key) { + if (i >= size) { + return; + } + if (i > 0) { + this.append(', '); + } + this.format([key, value]); + + i++; + }, this); + if (map.size > size) { + this.append(', ...'); + } + this.append(' )'); + } + + emitObject(obj) { + const ctor = obj.constructor; + const constructorName = + typeof ctor === 'function' && obj instanceof ctor + ? j$.fnNameFor(obj.constructor) + : 'null'; + + this.append(constructorName); + + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + return; + } + + this.append('({ '); + let first = true; + + const wasTruncated = this.iterateObject(obj, property => { + if (first) { + first = false; + } else { + this.append(', '); + } + + this.formatProperty(obj, property); + }); + + if (wasTruncated) { + this.append(', ...'); + } + + this.append(' })'); + } + + emitTypedArray(arr) { + const constructorName = j$.fnNameFor(arr.constructor); + const limitedArray = Array.prototype.slice.call( + arr, + 0, + j$.MAX_PRETTY_PRINT_ARRAY_LENGTH + ); + let itemsString = Array.prototype.join.call(limitedArray, ', '); + + if (limitedArray.length !== arr.length) { + itemsString += ', ...'; + } + + this.append(constructorName + ' [ ' + itemsString + ' ]'); + } + + emitDomElement(el) { + const tagName = el.tagName.toLowerCase(); + let out = '<' + tagName; + + for (const attr of el.attributes) { + out += ' ' + attr.name; + + if (attr.value !== '') { + out += '="' + attr.value + '"'; + } + } + + out += '>'; + + if (el.childElementCount !== 0 || el.textContent !== '') { + out += '...'; + } + + this.append(out); + } + + formatProperty(obj, property) { + if (typeof property === 'symbol') { + this.append(property.toString()); + } else { + this.append(property); + } + + this.append(': '); + this.format(obj[property]); + } + + append(value) { + // This check protects us from the rare case where an object has overriden + // `toString()` with an invalid implementation (returning a non-string). + if (typeof value !== 'string') { + value = Object.prototype.toString.call(value); + } + + const result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); + this.length += result.value.length; + this.stringParts.push(result.value); + + if (result.truncated) { + throw new MaxCharsReachedError(); + } + } + } + + function hasCustomToString(value) { + // value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g. + // iframe, web worker) + try { + return ( + j$.isFunction_(value.toString) && + value.toString !== Object.prototype.toString && + value.toString() !== Object.prototype.toString.call(value) + ); + } catch (e) { + // The custom toString() threw. + return true; + } + } + + function truncate(s, maxlen) { + if (s.length <= maxlen) { + return { value: s, truncated: false }; + } + + s = s.substring(0, maxlen - 4) + ' ...'; + return { value: s, truncated: true }; + } + + function MaxCharsReachedError() { + this.message = + 'Exceeded ' + + j$.MAX_PRETTY_PRINT_CHARS + + ' characters while pretty-printing a value'; + } + + MaxCharsReachedError.prototype = new Error(); + + function customFormat(value, customObjectFormatters) { + for (const formatter of customObjectFormatters) { + const result = formatter(value); + + if (result !== undefined) { + return result; + } + } + } + + return function(customObjectFormatters) { + customObjectFormatters = customObjectFormatters || []; + + const pp = function(value) { + const prettyPrinter = new SinglePrettyPrintRun( + customObjectFormatters, + pp + ); + prettyPrinter.format(value); + return prettyPrinter.stringParts.join(''); + }; + + pp.customFormat_ = function(value) { + return customFormat(value, customObjectFormatters); + }; + + return pp; + }; +}; + +getJasmineRequireObj().QueueRunner = function(j$) { + let nextid = 1; + + function StopExecutionError() {} + StopExecutionError.prototype = new Error(); + j$.StopExecutionError = StopExecutionError; + + function once(fn, onTwice) { + let called = false; + return function(arg) { + if (called) { + if (onTwice) { + onTwice(); + } + } else { + called = true; + // Direct call using single parameter, because cleanup/next does not need more + fn(arg); + } + return null; + }; + } + + function fallbackOnMultipleDone() { + console.error( + new Error( + "An asynchronous function called its 'done' " + + 'callback more than once, in a QueueRunner without a onMultipleDone ' + + 'handler.' + ) + ); + } + + function emptyFn() {} + + function QueueRunner(attrs) { + this.id_ = nextid++; + this.queueableFns = attrs.queueableFns || []; + this.onComplete = attrs.onComplete || emptyFn; + this.clearStack = + attrs.clearStack || + function(fn) { + fn(); + }; + this.onException = attrs.onException || emptyFn; + this.onMultipleDone = attrs.onMultipleDone || fallbackOnMultipleDone; + this.userContext = attrs.userContext || new j$.UserContext(); + this.timeout = attrs.timeout || { + setTimeout: setTimeout, + clearTimeout: clearTimeout + }; + this.fail = attrs.fail || emptyFn; + this.globalErrors = attrs.globalErrors || { + pushListener: emptyFn, + popListener: emptyFn + }; + + const SkipPolicy = attrs.SkipPolicy || j$.NeverSkipPolicy; + this.skipPolicy_ = new SkipPolicy(this.queueableFns); + this.errored_ = false; + + if (typeof this.onComplete !== 'function') { + throw new Error('invalid onComplete ' + JSON.stringify(this.onComplete)); + } + } + + QueueRunner.prototype.execute = function() { + this.handleFinalError = error => { + this.onException(error); + }; + this.globalErrors.pushListener(this.handleFinalError); + this.run(0); + }; + + QueueRunner.prototype.clearTimeout = function(timeoutId) { + Function.prototype.apply.apply(this.timeout.clearTimeout, [ + j$.getGlobal(), + [timeoutId] + ]); + }; + + QueueRunner.prototype.setTimeout = function(fn, timeout) { + return Function.prototype.apply.apply(this.timeout.setTimeout, [ + j$.getGlobal(), + [fn, timeout] + ]); + }; + + QueueRunner.prototype.attempt = function attempt(iterativeIndex) { + let timeoutId; + let timedOut; + let completedSynchronously = true; + + const onException = e => { + this.onException(e); + this.recordError_(iterativeIndex); + }; + + function handleError(error) { + // TODO probably shouldn't next() right away here. + // That makes debugging async failures much more confusing. + onException(error); + } + const cleanup = once(() => { + if (timeoutId !== void 0) { + this.clearTimeout(timeoutId); + } + this.globalErrors.popListener(handleError); + }); + const next = once( + err => { + cleanup(); + + if (typeof err !== 'undefined') { + if (!(err instanceof StopExecutionError) && !err.jasmineMessage) { + this.fail(err); + } + this.recordError_(iterativeIndex); + } + + const runNext = () => { + this.run(this.nextFnIx_(iterativeIndex)); + }; + + if (completedSynchronously) { + this.setTimeout(runNext); + } else { + runNext(); + } + }, + () => { + try { + if (!timedOut) { + this.onMultipleDone(); + } + } catch (error) { + // Any error we catch here is probably due to a bug in Jasmine, + // and it's not likely to end up anywhere useful if we let it + // propagate. Log it so it can at least show up when debugging. + console.error(error); + } + } + ); + timedOut = false; + const queueableFn = this.queueableFns[iterativeIndex]; + + next.fail = function nextFail() { + this.fail.apply(null, arguments); + this.recordError_(iterativeIndex); + next(); + }.bind(this); + + this.globalErrors.pushListener(handleError); + + if (queueableFn.timeout !== undefined) { + const timeoutInterval = + queueableFn.timeout || j$.DEFAULT_TIMEOUT_INTERVAL; + timeoutId = this.setTimeout(function() { + timedOut = true; + const error = new Error( + 'Timeout - Async function did not complete within ' + + timeoutInterval + + 'ms ' + + (queueableFn.timeout + ? '(custom timeout)' + : '(set by jasmine.DEFAULT_TIMEOUT_INTERVAL)') + ); + // TODO Need to decide what to do about a successful completion after a + // timeout. That should probably not be a deprecation, and maybe not + // an error in 4.0. (But a diagnostic of some sort might be helpful.) + onException(error); + next(); + }, timeoutInterval); + } + + try { + let maybeThenable; + + if (queueableFn.fn.length === 0) { + maybeThenable = queueableFn.fn.call(this.userContext); + + if (maybeThenable && j$.isFunction_(maybeThenable.then)) { + maybeThenable.then( + wrapInPromiseResolutionHandler(next), + onPromiseRejection + ); + completedSynchronously = false; + return { completedSynchronously: false }; + } + } else { + maybeThenable = queueableFn.fn.call(this.userContext, next); + this.diagnoseConflictingAsync_(queueableFn.fn, maybeThenable); + completedSynchronously = false; + return { completedSynchronously: false }; + } + } catch (e) { + onException(e); + this.recordError_(iterativeIndex); + } + + cleanup(); + return { completedSynchronously: true }; + + function onPromiseRejection(e) { + onException(e); + next(); + } + }; + + QueueRunner.prototype.run = function(recursiveIndex) { + const length = this.queueableFns.length; + + for ( + let iterativeIndex = recursiveIndex; + iterativeIndex < length; + iterativeIndex = this.nextFnIx_(iterativeIndex) + ) { + const result = this.attempt(iterativeIndex); + + if (!result.completedSynchronously) { + return; + } + } + + this.clearStack(() => { + this.globalErrors.popListener(this.handleFinalError); + + if (this.errored_) { + this.onComplete(new StopExecutionError()); + } else { + this.onComplete(); + } + }); + }; + + QueueRunner.prototype.nextFnIx_ = function(currentFnIx) { + const result = this.skipPolicy_.skipTo(currentFnIx); + + if (result === currentFnIx) { + throw new Error("Can't skip to the same queueable fn that just finished"); + } + + return result; + }; + + QueueRunner.prototype.recordError_ = function(currentFnIx) { + this.errored_ = true; + this.skipPolicy_.fnErrored(currentFnIx); + }; + + QueueRunner.prototype.diagnoseConflictingAsync_ = function(fn, retval) { + if (retval && j$.isFunction_(retval.then)) { + // Issue a warning that matches the user's code. + // Omit the stack trace because there's almost certainly no user code + // on the stack at this point. + if (j$.isAsyncFunction_(fn)) { + this.onException( + new Error( + 'An asynchronous before/it/after ' + + 'function was defined with the async keyword but also took a ' + + 'done callback. Either remove the done callback (recommended) or ' + + 'remove the async keyword.' + ) + ); + } else { + this.onException( + new Error( + 'An asynchronous before/it/after ' + + 'function took a done callback but also returned a promise. ' + + 'Either remove the done callback (recommended) or change the ' + + 'function to not return a promise.' + ) + ); + } + } + }; + + function wrapInPromiseResolutionHandler(fn) { + return function(maybeArg) { + if (j$.isError_(maybeArg)) { + fn(maybeArg); + } else { + fn(); + } + }; + } + + return QueueRunner; +}; + +getJasmineRequireObj().ReportDispatcher = function(j$) { + 'use strict'; + + function ReportDispatcher(methods, queueRunnerFactory, onLateError) { + const dispatchedMethods = methods || []; + + for (const method of dispatchedMethods) { + this[method] = (function(m) { + return function() { + return dispatch(m, arguments); + }; + })(method); + } + + let reporters = []; + let fallbackReporter = null; + + this.addReporter = function(reporter) { + reporters.push(reporter); + }; + + this.provideFallbackReporter = function(reporter) { + fallbackReporter = reporter; + }; + + this.clearReporters = function() { + reporters = []; + }; + + return this; + + function dispatch(method, args) { + if (reporters.length === 0 && fallbackReporter !== null) { + reporters.push(fallbackReporter); + } + const fns = []; + for (const reporter of reporters) { + addFn(fns, reporter, method, args); + } + + return new Promise(function(resolve) { + queueRunnerFactory({ + queueableFns: fns, + onComplete: resolve, + isReporter: true, + onMultipleDone: function() { + onLateError( + new Error( + "An asynchronous reporter callback called its 'done' callback " + + 'more than once.' + ) + ); + } + }); + }); + } + + function addFn(fns, reporter, method, args) { + const fn = reporter[method]; + if (!fn) { + return; + } + + const thisArgs = j$.util.cloneArgs(args); + if (fn.length <= 1) { + fns.push({ + fn: function() { + return fn.apply(reporter, thisArgs); + } + }); + } else { + fns.push({ + fn: function(done) { + return fn.apply(reporter, thisArgs.concat([done])); + } + }); + } + } + } + + return ReportDispatcher; +}; + +getJasmineRequireObj().reporterEvents = function() { + const events = [ + /** + * `jasmineStarted` is called after all of the specs have been loaded, but just before execution starts. + * @function + * @name Reporter#jasmineStarted + * @param {JasmineStartedInfo} suiteInfo Information about the full Jasmine suite that is being run + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'jasmineStarted', + /** + * When the entire suite has finished execution `jasmineDone` is called + * @function + * @name Reporter#jasmineDone + * @param {JasmineDoneInfo} suiteInfo Information about the full Jasmine suite that just finished running. + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'jasmineDone', + /** + * `suiteStarted` is invoked when a `describe` starts to run + * @function + * @name Reporter#suiteStarted + * @param {SuiteResult} result Information about the individual {@link describe} being run + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'suiteStarted', + /** + * `suiteDone` is invoked when all of the child specs and suites for a given suite have been run + * + * While jasmine doesn't require any specific functions, not defining a `suiteDone` will make it impossible for a reporter to know when a suite has failures in an `afterAll`. + * @function + * @name Reporter#suiteDone + * @param {SuiteResult} result + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'suiteDone', + /** + * `specStarted` is invoked when an `it` starts to run (including associated `beforeEach` functions) + * @function + * @name Reporter#specStarted + * @param {SpecResult} result Information about the individual {@link it} being run + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'specStarted', + /** + * `specDone` is invoked when an `it` and its associated `beforeEach` and `afterEach` functions have been run. + * + * While jasmine doesn't require any specific functions, not defining a `specDone` will make it impossible for a reporter to know when a spec has failed. + * @function + * @name Reporter#specDone + * @param {SpecResult} result + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + * @see async + */ + 'specDone' + ]; + Object.freeze(events); + return events; +}; + +getJasmineRequireObj().interface = function(jasmine, env) { + const jasmineInterface = { + /** + * Callback passed to parts of the Jasmine base interface. + * + * By default Jasmine assumes this function completes synchronously. + * If you have code that you need to test asynchronously, you can declare that you receive a `done` callback, return a Promise, or use the `async` keyword if it is supported in your environment. + * @callback implementationCallback + * @param {Function} [done] Used to specify to Jasmine that this callback is asynchronous and Jasmine should wait until it has been called before moving on. + * @returns {} Optionally return a Promise instead of using `done` to cause Jasmine to wait for completion. + */ + + /** + * Create a group of specs (often called a suite). + * + * Calls to `describe` can be nested within other calls to compose your suite as a tree. + * @name describe + * @since 1.3.0 + * @function + * @global + * @param {String} description Textual description of the group + * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs + */ + describe: function(description, specDefinitions) { + return env.describe(description, specDefinitions); + }, + + /** + * A temporarily disabled [`describe`]{@link describe} + * + * Specs within an `xdescribe` will be marked pending and not executed + * @name xdescribe + * @since 1.3.0 + * @function + * @global + * @param {String} description Textual description of the group + * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs + */ + xdescribe: function(description, specDefinitions) { + return env.xdescribe(description, specDefinitions); + }, + + /** + * A focused [`describe`]{@link describe} + * + * If suites or specs are focused, only those that are focused will be executed + * @see fit + * @name fdescribe + * @since 2.1.0 + * @function + * @global + * @param {String} description Textual description of the group + * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites and specs + */ + fdescribe: function(description, specDefinitions) { + return env.fdescribe(description, specDefinitions); + }, + + /** + * Define a single spec. A spec should contain one or more {@link expect|expectations} that test the state of the code. + * + * A spec whose expectations all succeed will be passing and a spec with any failures will fail. + * The name `it` is a pronoun for the test target, not an abbreviation of anything. It makes the + * spec more readable by connecting the function name `it` and the argument `description` as a + * complete sentence. + * @name it + * @since 1.3.0 + * @function + * @global + * @param {String} description Textual description of what this spec is checking + * @param {implementationCallback} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. + * @see async + */ + it: function() { + return env.it.apply(env, arguments); + }, + + /** + * A temporarily disabled [`it`]{@link it} + * + * The spec will report as `pending` and will not be executed. + * @name xit + * @since 1.3.0 + * @function + * @global + * @param {String} description Textual description of what this spec is checking. + * @param {implementationCallback} [testFunction] Function that contains the code of your test. Will not be executed. + */ + xit: function() { + return env.xit.apply(env, arguments); + }, + + /** + * A focused [`it`]{@link it} + * + * If suites or specs are focused, only those that are focused will be executed. + * @name fit + * @since 2.1.0 + * @function + * @global + * @param {String} description Textual description of what this spec is checking. + * @param {implementationCallback} testFunction Function that contains the code of your test. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec. + * @see async + */ + fit: function() { + return env.fit.apply(env, arguments); + }, + + /** + * Run some shared setup before each of the specs in the {@link describe} in which it is called. + * @name beforeEach + * @since 1.3.0 + * @function + * @global + * @param {implementationCallback} [function] Function that contains the code to setup your specs. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach. + * @see async + */ + beforeEach: function() { + return env.beforeEach.apply(env, arguments); + }, + + /** + * Run some shared teardown after each of the specs in the {@link describe} in which it is called. + * @name afterEach + * @since 1.3.0 + * @function + * @global + * @param {implementationCallback} [function] Function that contains the code to teardown your specs. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach. + * @see async + */ + afterEach: function() { + return env.afterEach.apply(env, arguments); + }, + + /** + * Run some shared setup once before all of the specs in the {@link describe} are run. + * + * _Note:_ Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. + * @name beforeAll + * @since 2.1.0 + * @function + * @global + * @param {implementationCallback} [function] Function that contains the code to setup your specs. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll. + * @see async + */ + beforeAll: function() { + return env.beforeAll.apply(env, arguments); + }, + + /** + * Run some shared teardown once after all of the specs in the {@link describe} are run. + * + * _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail. + * @name afterAll + * @since 2.1.0 + * @function + * @global + * @param {implementationCallback} [function] Function that contains the code to teardown your specs. + * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll. + * @see async + */ + afterAll: function() { + return env.afterAll.apply(env, arguments); + }, + + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SpecResult} + * @name setSpecProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + setSpecProperty: function(key, value) { + return env.setSpecProperty(key, value); + }, + + /** + * Sets a user-defined property that will be provided to reporters as part of the properties field of {@link SuiteResult} + * @name setSuiteProperty + * @since 3.6.0 + * @function + * @param {String} key The name of the property + * @param {*} value The value of the property + */ + setSuiteProperty: function(key, value) { + return env.setSuiteProperty(key, value); + }, + + /** + * Create an expectation for a spec. + * @name expect + * @since 1.3.0 + * @function + * @global + * @param {Object} actual - Actual computed value to test expectations against. + * @return {matchers} + */ + expect: function(actual) { + return env.expect(actual); + }, + + /** + * Create an asynchronous expectation for a spec. Note that the matchers + * that are provided by an asynchronous expectation all return promises + * which must be either returned from the spec or waited for using `await` + * in order for Jasmine to associate them with the correct spec. + * @name expectAsync + * @since 3.3.0 + * @function + * @global + * @param {Object} actual - Actual computed value to test expectations against. + * @return {async-matchers} + * @example + * await expectAsync(somePromise).toBeResolved(); + * @example + * return expectAsync(somePromise).toBeResolved(); + */ + expectAsync: function(actual) { + return env.expectAsync(actual); + }, + + /** + * Create an asynchronous expectation for a spec and throw an error if it fails. + * + * This is intended to allow Jasmine matchers to be used with tools like + * testing-library's `waitFor`, which expect matcher failures to throw + * exceptions and not trigger a spec failure if the exception is caught. + * It can also be used to integration-test custom matchers. + * + * If the resulting expectation fails, a {@link ThrowUnlessFailure} will be + * thrown. A failed expectation will not result in a spec failure unless the + * exception propagates back to Jasmine, either via the call stack or via + * the global unhandled exception/unhandled promise rejection events. + * @name throwUnlessAsync + * @since 5.1.0 + * @function + * @param actual + * @global + * @param {Object} actual - Actual computed value to test expectations against. + * @return {matchers} + */ + throwUnlessAsync: function(actual) { + return env.throwUnlessAsync(actual); + }, + + /** + * Create an expectation for a spec and throw an error if it fails. + * + * This is intended to allow Jasmine matchers to be used with tools like + * testing-library's `waitFor`, which expect matcher failures to throw + * exceptions and not trigger a spec failure if the exception is caught. + * It can also be used to integration-test custom matchers. + * + * If the resulting expectation fails, a {@link ThrowUnlessFailure} will be + * thrown. A failed expectation will not result in a spec failure unless the + * exception propagates back to Jasmine, either via the call stack or via + * the global unhandled exception/unhandled promise rejection events. + * @name throwUnless + * @since 5.1.0 + * @function + * @param actual + * @global + * @param {Object} actual - Actual computed value to test expectations against. + * @return {matchers} + */ + throwUnless: function(actual) { + return env.throwUnless(actual); + }, + + /** + * Mark a spec as pending, expectation results will be ignored. + * @name pending + * @since 2.0.0 + * @function + * @global + * @param {String} [message] - Reason the spec is pending. + */ + pending: function() { + return env.pending.apply(env, arguments); + }, + + /** + * Explicitly mark a spec as failed. + * @name fail + * @since 2.1.0 + * @function + * @global + * @param {String|Error} [error] - Reason for the failure. + */ + fail: function() { + return env.fail.apply(env, arguments); + }, + + /** + * Install a spy onto an existing object. + * @name spyOn + * @since 1.3.0 + * @function + * @global + * @param {Object} obj - The object upon which to install the {@link Spy}. + * @param {String} methodName - The name of the method to replace with a {@link Spy}. + * @returns {Spy} + */ + spyOn: function(obj, methodName) { + return env.spyOn(obj, methodName); + }, + + /** + * Install a spy on a property installed with `Object.defineProperty` onto an existing object. + * @name spyOnProperty + * @since 2.6.0 + * @function + * @global + * @param {Object} obj - The object upon which to install the {@link Spy} + * @param {String} propertyName - The name of the property to replace with a {@link Spy}. + * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on. + * @returns {Spy} + */ + spyOnProperty: function(obj, methodName, accessType) { + return env.spyOnProperty(obj, methodName, accessType); + }, + + /** + * Installs spies on all writable and configurable properties of an object. + * @name spyOnAllFunctions + * @since 3.2.1 + * @function + * @global + * @param {Object} obj - The object upon which to install the {@link Spy}s + * @param {boolean} includeNonEnumerable - Whether or not to add spies to non-enumerable properties + * @returns {Object} the spied object + */ + spyOnAllFunctions: function(obj, includeNonEnumerable) { + return env.spyOnAllFunctions(obj, includeNonEnumerable); + }, + + jsApiReporter: new jasmine.JsApiReporter({ + timer: new jasmine.Timer() + }), + + /** + * @namespace jasmine + */ + jasmine: jasmine + }; + + /** + * Add a custom equality tester for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.addCustomEqualityTester + * @since 2.0.0 + * @function + * @param {Function} tester - A function which takes two arguments to compare and returns a `true` or `false` comparison result if it knows how to compare them, and `undefined` otherwise. + * @see custom_equality + */ + jasmine.addCustomEqualityTester = function(tester) { + env.addCustomEqualityTester(tester); + }; + + /** + * Add custom matchers for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.addMatchers + * @since 2.0.0 + * @function + * @param {Object} matchers - Keys from this object will be the new matcher names. + * @see custom_matcher + */ + jasmine.addMatchers = function(matchers) { + return env.addMatchers(matchers); + }; + + /** + * Add custom async matchers for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.addAsyncMatchers + * @since 3.5.0 + * @function + * @param {Object} matchers - Keys from this object will be the new async matcher names. + * @see custom_matcher + */ + jasmine.addAsyncMatchers = function(matchers) { + return env.addAsyncMatchers(matchers); + }; + + /** + * Add a custom object formatter for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.addCustomObjectFormatter + * @since 3.6.0 + * @function + * @param {Function} formatter - A function which takes a value to format and returns a string if it knows how to format it, and `undefined` otherwise. + * @see custom_object_formatters + */ + jasmine.addCustomObjectFormatter = function(formatter) { + return env.addCustomObjectFormatter(formatter); + }; + + /** + * Get the currently booted mock {Clock} for this Jasmine environment. + * @name jasmine.clock + * @since 2.0.0 + * @function + * @returns {Clock} + */ + jasmine.clock = function() { + return env.clock; + }; + + /** + * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it. + * @name jasmine.createSpy + * @since 1.3.0 + * @function + * @param {String} [name] - Name to give the spy. This will be displayed in failure messages. + * @param {Function} [originalFn] - The "real" function. This will + * be used for subsequent calls to the spy after you call + * `mySpy.and.callThrough()`. In most cases you should omit this parameter. + * The usual way to supply an original function is to call {@link spyOn} + * instead of createSpy. + * @return {Spy} + */ + jasmine.createSpy = function(name, originalFn) { + return env.createSpy(name, originalFn); + }; + + /** + * Create an object with multiple {@link Spy}s as its members. + * @name jasmine.createSpyObj + * @since 1.3.0 + * @function + * @param {String} [baseName] - Base name for the spies in the object. + * @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}. + * @param {String[]|Object} [propertyNames] - Array of property names to create spies for, or Object whose keys will be propertynames and values the {@link Spy#and#returnValue|returnValue}. + * @return {Object} + */ + jasmine.createSpyObj = function(baseName, methodNames, propertyNames) { + return env.createSpyObj(baseName, methodNames, propertyNames); + }; + + /** + * Add a custom spy strategy for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.addSpyStrategy + * @since 3.5.0 + * @function + * @param {String} name - The name of the strategy (i.e. what you call from `and`) + * @param {Function} factory - Factory function that returns the plan to be executed. + */ + jasmine.addSpyStrategy = function(name, factory) { + return env.addSpyStrategy(name, factory); + }; + + /** + * Set the default spy strategy for the current scope of specs. + * + * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}. + * @name jasmine.setDefaultSpyStrategy + * @function + * @param {Function} defaultStrategyFn - a function that assigns a strategy + * @example + * beforeEach(function() { + * jasmine.setDefaultSpyStrategy(and => and.returnValue(true)); + * }); + */ + jasmine.setDefaultSpyStrategy = function(defaultStrategyFn) { + return env.setDefaultSpyStrategy(defaultStrategyFn); + }; + + return jasmineInterface; +}; + +getJasmineRequireObj().RunableResources = function(j$) { + class RunableResources { + constructor(options) { + this.byRunableId_ = {}; + this.getCurrentRunableId_ = options.getCurrentRunableId; + this.globalErrors_ = options.globalErrors; + + this.spyFactory = new j$.SpyFactory( + () => { + if (this.getCurrentRunableId_()) { + return this.customSpyStrategies(); + } else { + return {}; + } + }, + () => this.defaultSpyStrategy(), + () => this.makeMatchersUtil() + ); + + this.spyRegistry = new j$.SpyRegistry({ + currentSpies: () => this.spies(), + createSpy: (name, originalFn) => + this.spyFactory.createSpy(name, originalFn) + }); + } + + initForRunable(runableId, parentId) { + const newRes = (this.byRunableId_[runableId] = { + customEqualityTesters: [], + customMatchers: {}, + customAsyncMatchers: {}, + customSpyStrategies: {}, + customObjectFormatters: [], + defaultSpyStrategy: undefined, + spies: [] + }); + + const parentRes = this.byRunableId_[parentId]; + + if (parentRes) { + newRes.defaultSpyStrategy = parentRes.defaultSpyStrategy; + const toClone = [ + 'customEqualityTesters', + 'customMatchers', + 'customAsyncMatchers', + 'customObjectFormatters', + 'customSpyStrategies' + ]; + + for (const k of toClone) { + newRes[k] = j$.util.clone(parentRes[k]); + } + } + } + + clearForRunable(runableId) { + this.globalErrors_.removeOverrideListener(); + this.spyRegistry.clearSpies(); + delete this.byRunableId_[runableId]; + } + + spies() { + return this.forCurrentRunable_( + 'Spies must be created in a before function or a spec' + ).spies; + } + + defaultSpyStrategy() { + if (!this.getCurrentRunableId_()) { + return undefined; + } + + return this.byRunableId_[this.getCurrentRunableId_()].defaultSpyStrategy; + } + + setDefaultSpyStrategy(fn) { + this.forCurrentRunable_( + 'Default spy strategy must be set in a before function or a spec' + ).defaultSpyStrategy = fn; + } + + customSpyStrategies() { + return this.forCurrentRunable_( + 'Custom spy strategies must be added in a before function or a spec' + ).customSpyStrategies; + } + + customEqualityTesters() { + return this.forCurrentRunable_( + 'Custom Equalities must be added in a before function or a spec' + ).customEqualityTesters; + } + + customMatchers() { + return this.forCurrentRunable_( + 'Matchers must be added in a before function or a spec' + ).customMatchers; + } + + addCustomMatchers(matchersToAdd) { + const matchers = this.customMatchers(); + + for (const name in matchersToAdd) { + matchers[name] = matchersToAdd[name]; + } + } + + customAsyncMatchers() { + return this.forCurrentRunable_( + 'Async Matchers must be added in a before function or a spec' + ).customAsyncMatchers; + } + + addCustomAsyncMatchers(matchersToAdd) { + const matchers = this.customAsyncMatchers(); + + for (const name in matchersToAdd) { + matchers[name] = matchersToAdd[name]; + } + } + + customObjectFormatters() { + return this.forCurrentRunable_( + 'Custom object formatters must be added in a before function or a spec' + ).customObjectFormatters; + } + + makePrettyPrinter() { + return j$.makePrettyPrinter(this.customObjectFormatters()); + } + + makeMatchersUtil() { + if (this.getCurrentRunableId_()) { + return new j$.MatchersUtil({ + customTesters: this.customEqualityTesters(), + pp: this.makePrettyPrinter() + }); + } else { + return new j$.MatchersUtil({ pp: j$.basicPrettyPrinter_ }); + } + } + + forCurrentRunable_(errorMsg) { + const resources = this.byRunableId_[this.getCurrentRunableId_()]; + + if (!resources && errorMsg) { + throw new Error(errorMsg); + } + + return resources; + } + } + + return RunableResources; +}; + +getJasmineRequireObj().Runner = function(j$) { + class Runner { + constructor(options) { + this.topSuite_ = options.topSuite; + // TODO use names that read like getters + this.totalSpecsDefined_ = options.totalSpecsDefined; + this.focusedRunables_ = options.focusedRunables; + this.runableResources_ = options.runableResources; + this.queueRunnerFactory_ = options.queueRunnerFactory; + this.reporter_ = options.reporter; + this.getConfig_ = options.getConfig; + this.reportSpecDone_ = options.reportSpecDone; + this.hasFailures = false; + this.executedBefore_ = false; + + this.currentlyExecutingSuites_ = []; + this.currentSpec = null; + } + + currentRunable() { + return this.currentSpec || this.currentSuite(); + } + + currentSuite() { + return this.currentlyExecutingSuites_[ + this.currentlyExecutingSuites_.length - 1 + ]; + } + + parallelReset() { + this.executedBefore_ = false; + } + + async execute(runablesToRun) { + if (this.executedBefore_) { + this.topSuite_.reset(); + } + this.executedBefore_ = true; + + this.hasFailures = false; + const focusedRunables = this.focusedRunables_(); + const config = this.getConfig_(); + + if (!runablesToRun) { + if (focusedRunables.length) { + runablesToRun = focusedRunables; + } else { + runablesToRun = [this.topSuite_.id]; + } + } + + const order = new j$.Order({ + random: config.random, + seed: j$.isNumber_(config.seed) ? config.seed + '' : config.seed + }); + + const processor = new j$.TreeProcessor({ + tree: this.topSuite_, + runnableIds: runablesToRun, + queueRunnerFactory: options => { + if (options.isLeaf) { + // A spec + options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; + } else { + // A suite + if (config.stopOnSpecFailure) { + options.SkipPolicy = j$.CompleteOnFirstErrorSkipPolicy; + } else { + options.SkipPolicy = j$.SkipAfterBeforeAllErrorPolicy; + } + } + + return this.queueRunnerFactory_(options); + }, + failSpecWithNoExpectations: config.failSpecWithNoExpectations, + nodeStart: (suite, next) => { + this.currentlyExecutingSuites_.push(suite); + this.runableResources_.initForRunable(suite.id, suite.parentSuite.id); + this.reporter_.suiteStarted(suite.result).then(next); + suite.startTimer(); + }, + nodeComplete: (suite, result, next) => { + if (suite !== this.currentSuite()) { + throw new Error('Tried to complete the wrong suite'); + } + + this.runableResources_.clearForRunable(suite.id); + this.currentlyExecutingSuites_.pop(); + + if (result.status === 'failed') { + this.hasFailures = true; + } + suite.endTimer(); + + if (suite.hadBeforeAllFailure) { + this.reportChildrenOfBeforeAllFailure_(suite).then(() => { + this.reportSuiteDone_(suite, result, next); + }); + } else { + this.reportSuiteDone_(suite, result, next); + } + }, + orderChildren: function(node) { + return order.sort(node.children); + }, + excludeNode: function(spec) { + return !config.specFilter(spec); + } + }); + + if (!processor.processTree().valid) { + throw new Error( + 'Invalid order: would cause a beforeAll or afterAll to be run multiple times' + ); + } + + return this.execute2_(runablesToRun, order, processor); + } + + async execute2_(runablesToRun, order, processor) { + const totalSpecsDefined = this.totalSpecsDefined_(); + + this.runableResources_.initForRunable(this.topSuite_.id); + const jasmineTimer = new j$.Timer(); + jasmineTimer.start(); + + /** + * Information passed to the {@link Reporter#jasmineStarted} event. + * @typedef JasmineStartedInfo + * @property {Int} totalSpecsDefined - The total number of specs defined in this suite. Note that this property is not present when Jasmine is run in parallel mode. + * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. Note that this property is not present when Jasmine is run in parallel mode. + * @property {Boolean} parallel - Whether Jasmine is being run in parallel mode. + * @since 2.0.0 + */ + await this.reporter_.jasmineStarted({ + // In parallel mode, the jasmineStarted event is separately dispatched + // by jasmine-npm. This event only reaches reporters in non-parallel. + totalSpecsDefined, + order: order, + parallel: false + }); + + this.currentlyExecutingSuites_.push(this.topSuite_); + await processor.execute(); + + if (this.topSuite_.hadBeforeAllFailure) { + await this.reportChildrenOfBeforeAllFailure_(this.topSuite_); + } + + this.runableResources_.clearForRunable(this.topSuite_.id); + this.currentlyExecutingSuites_.pop(); + let overallStatus, incompleteReason, incompleteCode; + + if ( + this.hasFailures || + this.topSuite_.result.failedExpectations.length > 0 + ) { + overallStatus = 'failed'; + } else if (this.focusedRunables_().length > 0) { + overallStatus = 'incomplete'; + incompleteReason = 'fit() or fdescribe() was found'; + incompleteCode = 'focused'; + } else if (totalSpecsDefined === 0) { + overallStatus = 'incomplete'; + incompleteReason = 'No specs found'; + incompleteCode = 'noSpecsFound'; + } else { + overallStatus = 'passed'; + } + + /** + * Information passed to the {@link Reporter#jasmineDone} event. + * @typedef JasmineDoneInfo + * @property {OverallStatus} overallStatus - The overall result of the suite: 'passed', 'failed', or 'incomplete'. + * @property {Int} totalTime - The total time (in ms) that it took to execute the suite + * @property {String} incompleteReason - Human-readable explanation of why the suite was incomplete. + * @property {String} incompleteCode - Machine-readable explanation of why the suite was incomplete: 'focused', 'noSpecsFound', or undefined. + * @property {Order} order - Information about the ordering (random or not) of this execution of the suite. Note that this property is not present when Jasmine is run in parallel mode. + * @property {Int} numWorkers - Number of parallel workers. Note that this property is only present when Jasmine is run in parallel mode. + * @property {Expectation[]} failedExpectations - List of expectations that failed in an {@link afterAll} at the global level. + * @property {Expectation[]} deprecationWarnings - List of deprecation warnings that occurred at the global level. + * @since 2.4.0 + */ + const jasmineDoneInfo = { + overallStatus: overallStatus, + totalTime: jasmineTimer.elapsed(), + incompleteReason: incompleteReason, + incompleteCode: incompleteCode, + order: order, + failedExpectations: this.topSuite_.result.failedExpectations, + deprecationWarnings: this.topSuite_.result.deprecationWarnings + }; + this.topSuite_.reportedDone = true; + await this.reporter_.jasmineDone(jasmineDoneInfo); + return jasmineDoneInfo; + } + + reportSuiteDone_(suite, result, next) { + suite.reportedDone = true; + this.reporter_.suiteDone(result).then(next); + } + + async reportChildrenOfBeforeAllFailure_(suite) { + for (const child of suite.children) { + if (child instanceof j$.Suite) { + await this.reporter_.suiteStarted(child.result); + await this.reportChildrenOfBeforeAllFailure_(child); + + // Marking the suite passed is consistent with how suites that + // contain failed specs but no suite-level failures are reported. + child.result.status = 'passed'; + + await this.reporter_.suiteDone(child.result); + } else { + /* a spec */ + await this.reporter_.specStarted(child.result); + + child.addExpectationResult( + false, + { + passed: false, + message: + 'Not run because a beforeAll function failed. The ' + + 'beforeAll failure will be reported on the suite that ' + + 'caused it.' + }, + true + ); + child.result.status = 'failed'; + + await new Promise(resolve => { + this.reportSpecDone_(child, child.result, resolve); + }); + } + } + } + } + + return Runner; +}; + +getJasmineRequireObj().SkipAfterBeforeAllErrorPolicy = function(j$) { + function SkipAfterBeforeAllErrorPolicy(queueableFns) { + this.queueableFns_ = queueableFns; + this.skipping_ = false; + } + + SkipAfterBeforeAllErrorPolicy.prototype.skipTo = function(lastRanFnIx) { + if (this.skipping_) { + return this.nextAfterAllAfter_(lastRanFnIx); + } else { + return lastRanFnIx + 1; + } + }; + + SkipAfterBeforeAllErrorPolicy.prototype.nextAfterAllAfter_ = function(i) { + for ( + i++; + i < this.queueableFns_.length && + this.queueableFns_[i].type !== 'afterAll'; + i++ + ) {} + return i; + }; + + SkipAfterBeforeAllErrorPolicy.prototype.fnErrored = function(fnIx) { + if (this.queueableFns_[fnIx].type === 'beforeAll') { + this.skipping_ = true; + // Failures need to be reported for each contained spec. But we can't do + // that from here because reporting is async. This function isn't async + // (and can't be without greatly complicating QueueRunner). Mark the + // failure so that the code that reports the suite result (which is + // already async) can detect the failure and report the specs. + this.queueableFns_[fnIx].suite.hadBeforeAllFailure = true; + } + }; + + return SkipAfterBeforeAllErrorPolicy; +}; + +getJasmineRequireObj().Spy = function(j$) { + const nextOrder = (function() { + let order = 0; + + return function() { + return order++; + }; + })(); + + /** + * @classdesc _Note:_ Do not construct this directly. Use {@link spyOn}, + * {@link spyOnProperty}, {@link jasmine.createSpy}, or + * {@link jasmine.createSpyObj} instead. + * @class Spy + * @hideconstructor + */ + function Spy(name, matchersUtil, optionals) { + const spy = function(context, args, invokeNew) { + /** + * @name Spy.callData + * @property {object} object - `this` context for the invocation. + * @property {number} invocationOrder - Order of the invocation. + * @property {Array} args - The arguments passed for this invocation. + * @property returnValue - The value that was returned from this invocation. + */ + const callData = { + object: context, + invocationOrder: nextOrder(), + args: Array.prototype.slice.apply(args) + }; + + callTracker.track(callData); + const returnValue = strategyDispatcher.exec(context, args, invokeNew); + callData.returnValue = returnValue; + + return returnValue; + }; + const { originalFn, customStrategies, defaultStrategyFn } = optionals || {}; + + const numArgs = typeof originalFn === 'function' ? originalFn.length : 0, + wrapper = makeFunc(numArgs, function(context, args, invokeNew) { + return spy(context, args, invokeNew); + }), + strategyDispatcher = new SpyStrategyDispatcher( + { + name: name, + fn: originalFn, + getSpy: function() { + return wrapper; + }, + customStrategies: customStrategies + }, + matchersUtil + ), + callTracker = new j$.CallTracker(); + + function makeFunc(length, fn) { + switch (length) { + case 1: + return function wrap1(a) { + return fn(this, arguments, this instanceof wrap1); + }; + case 2: + return function wrap2(a, b) { + return fn(this, arguments, this instanceof wrap2); + }; + case 3: + return function wrap3(a, b, c) { + return fn(this, arguments, this instanceof wrap3); + }; + case 4: + return function wrap4(a, b, c, d) { + return fn(this, arguments, this instanceof wrap4); + }; + case 5: + return function wrap5(a, b, c, d, e) { + return fn(this, arguments, this instanceof wrap5); + }; + case 6: + return function wrap6(a, b, c, d, e, f) { + return fn(this, arguments, this instanceof wrap6); + }; + case 7: + return function wrap7(a, b, c, d, e, f, g) { + return fn(this, arguments, this instanceof wrap7); + }; + case 8: + return function wrap8(a, b, c, d, e, f, g, h) { + return fn(this, arguments, this instanceof wrap8); + }; + case 9: + return function wrap9(a, b, c, d, e, f, g, h, i) { + return fn(this, arguments, this instanceof wrap9); + }; + default: + return function wrap() { + return fn(this, arguments, this instanceof wrap); + }; + } + } + + for (const prop in originalFn) { + if (prop === 'and' || prop === 'calls') { + throw new Error( + "Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon" + ); + } + + wrapper[prop] = originalFn[prop]; + } + + /** + * @member {SpyStrategy} - Accesses the default strategy for the spy. This strategy will be used + * whenever the spy is called with arguments that don't match any strategy + * created with {@link Spy#withArgs}. + * @name Spy#and + * @since 2.0.0 + * @example + * spyOn(someObj, 'func').and.returnValue(42); + */ + wrapper.and = strategyDispatcher.and; + /** + * Specifies a strategy to be used for calls to the spy that have the + * specified arguments. + * @name Spy#withArgs + * @since 3.0.0 + * @function + * @param {...*} args - The arguments to match + * @type {SpyStrategy} + * @example + * spyOn(someObj, 'func').withArgs(1, 2, 3).and.returnValue(42); + * someObj.func(1, 2, 3); // returns 42 + */ + wrapper.withArgs = function() { + return strategyDispatcher.withArgs.apply(strategyDispatcher, arguments); + }; + wrapper.calls = callTracker; + + if (defaultStrategyFn) { + defaultStrategyFn(wrapper.and); + } + + return wrapper; + } + + function SpyStrategyDispatcher(strategyArgs, matchersUtil) { + const baseStrategy = new j$.SpyStrategy(strategyArgs); + const argsStrategies = new StrategyDict(function() { + return new j$.SpyStrategy(strategyArgs); + }, matchersUtil); + + this.and = baseStrategy; + + this.exec = function(spy, args, invokeNew) { + let strategy = argsStrategies.get(args); + + if (!strategy) { + if (argsStrategies.any() && !baseStrategy.isConfigured()) { + throw new Error( + "Spy '" + + strategyArgs.name + + "' received a call with arguments " + + j$.basicPrettyPrinter_(Array.prototype.slice.call(args)) + + ' but all configured strategies specify other arguments.' + ); + } else { + strategy = baseStrategy; + } + } + + return strategy.exec(spy, args, invokeNew); + }; + + this.withArgs = function() { + return { and: argsStrategies.getOrCreate(arguments) }; + }; + } + + function StrategyDict(strategyFactory, matchersUtil) { + this.strategies = []; + this.strategyFactory = strategyFactory; + this.matchersUtil = matchersUtil; + } + + StrategyDict.prototype.any = function() { + return this.strategies.length > 0; + }; + + StrategyDict.prototype.getOrCreate = function(args) { + let strategy = this.get(args); + + if (!strategy) { + strategy = this.strategyFactory(); + this.strategies.push({ + args: args, + strategy: strategy + }); + } + + return strategy; + }; + + StrategyDict.prototype.get = function(args) { + for (let i = 0; i < this.strategies.length; i++) { + if (this.matchersUtil.equals(args, this.strategies[i].args)) { + return this.strategies[i].strategy; + } + } + }; + + return Spy; +}; + +getJasmineRequireObj().SpyFactory = function(j$) { + function SpyFactory( + getCustomStrategies, + getDefaultStrategyFn, + getMatchersUtil + ) { + this.createSpy = function(name, originalFn) { + if (j$.isFunction_(name) && originalFn === undefined) { + originalFn = name; + name = originalFn.name; + } + + return j$.Spy(name, getMatchersUtil(), { + originalFn, + customStrategies: getCustomStrategies(), + defaultStrategyFn: getDefaultStrategyFn() + }); + }; + + this.createSpyObj = function(baseName, methodNames, propertyNames) { + const baseNameIsCollection = + j$.isObject_(baseName) || j$.isArray_(baseName); + + if (baseNameIsCollection) { + propertyNames = methodNames; + methodNames = baseName; + baseName = 'unknown'; + } + + const obj = {}; + + const methods = normalizeKeyValues(methodNames); + for (let i = 0; i < methods.length; i++) { + const spy = (obj[methods[i][0]] = this.createSpy( + baseName + '.' + methods[i][0] + )); + if (methods[i].length > 1) { + spy.and.returnValue(methods[i][1]); + } + } + + const properties = normalizeKeyValues(propertyNames); + for (let i = 0; i < properties.length; i++) { + const descriptor = { + enumerable: true, + get: this.createSpy(baseName + '.' + properties[i][0] + '.get'), + set: this.createSpy(baseName + '.' + properties[i][0] + '.set') + }; + if (properties[i].length > 1) { + descriptor.get.and.returnValue(properties[i][1]); + descriptor.set.and.returnValue(properties[i][1]); + } + Object.defineProperty(obj, properties[i][0], descriptor); + } + + if (methods.length === 0 && properties.length === 0) { + throw 'createSpyObj requires a non-empty array or object of method names to create spies for'; + } + + return obj; + }; + } + + function normalizeKeyValues(object) { + const result = []; + if (j$.isArray_(object)) { + for (let i = 0; i < object.length; i++) { + result.push([object[i]]); + } + } else if (j$.isObject_(object)) { + for (const key in object) { + if (object.hasOwnProperty(key)) { + result.push([key, object[key]]); + } + } + } + return result; + } + + return SpyFactory; +}; + +getJasmineRequireObj().SpyRegistry = function(j$) { + const spyOnMsg = j$.formatErrorMsg( + '', + 'spyOn(, )' + ); + const spyOnPropertyMsg = j$.formatErrorMsg( + '', + 'spyOnProperty(, , [accessType])' + ); + + function SpyRegistry(options) { + options = options || {}; + const global = options.global || j$.getGlobal(); + const createSpy = options.createSpy; + const currentSpies = + options.currentSpies || + function() { + return []; + }; + + this.allowRespy = function(allow) { + this.respy = allow; + }; + + this.spyOn = function(obj, methodName) { + const getErrorMsg = spyOnMsg; + + if (j$.util.isUndefined(obj) || obj === null) { + throw new Error( + getErrorMsg( + 'could not find an object to spy upon for ' + methodName + '()' + ) + ); + } + + if (j$.util.isUndefined(methodName) || methodName === null) { + throw new Error(getErrorMsg('No method name supplied')); + } + + if (j$.util.isUndefined(obj[methodName])) { + throw new Error(getErrorMsg(methodName + '() method does not exist')); + } + + if (obj[methodName] && j$.isSpy(obj[methodName])) { + if (this.respy) { + return obj[methodName]; + } else { + throw new Error( + getErrorMsg(methodName + ' has already been spied upon') + ); + } + } + + const descriptor = Object.getOwnPropertyDescriptor(obj, methodName); + + if (descriptor && !(descriptor.writable || descriptor.set)) { + throw new Error( + getErrorMsg(methodName + ' is not declared writable or has no setter') + ); + } + + const originalMethod = obj[methodName]; + const spiedMethod = createSpy(methodName, originalMethod); + let restoreStrategy; + + if ( + Object.prototype.hasOwnProperty.call(obj, methodName) || + (obj === global && methodName === 'onerror') + ) { + restoreStrategy = function() { + obj[methodName] = originalMethod; + }; + } else { + restoreStrategy = function() { + if (!delete obj[methodName]) { + obj[methodName] = originalMethod; + } + }; + } + + currentSpies().push({ + restoreObjectToOriginalState: restoreStrategy + }); + + obj[methodName] = spiedMethod; + + return spiedMethod; + }; + + this.spyOnProperty = function(obj, propertyName, accessType) { + const getErrorMsg = spyOnPropertyMsg; + + accessType = accessType || 'get'; + + if (j$.util.isUndefined(obj)) { + throw new Error( + getErrorMsg( + 'spyOn could not find an object to spy upon for ' + + propertyName + + '' + ) + ); + } + + if (j$.util.isUndefined(propertyName)) { + throw new Error(getErrorMsg('No property name supplied')); + } + + const descriptor = j$.util.getPropertyDescriptor(obj, propertyName); + + if (!descriptor) { + throw new Error(getErrorMsg(propertyName + ' property does not exist')); + } + + if (!descriptor.configurable) { + throw new Error( + getErrorMsg(propertyName + ' is not declared configurable') + ); + } + + if (!descriptor[accessType]) { + throw new Error( + getErrorMsg( + 'Property ' + + propertyName + + ' does not have access type ' + + accessType + ) + ); + } + + if (j$.isSpy(descriptor[accessType])) { + if (this.respy) { + return descriptor[accessType]; + } else { + throw new Error( + getErrorMsg( + propertyName + '#' + accessType + ' has already been spied upon' + ) + ); + } + } + + const originalDescriptor = j$.util.clone(descriptor); + const spy = createSpy(propertyName, descriptor[accessType]); + let restoreStrategy; + + if (Object.prototype.hasOwnProperty.call(obj, propertyName)) { + restoreStrategy = function() { + Object.defineProperty(obj, propertyName, originalDescriptor); + }; + } else { + restoreStrategy = function() { + delete obj[propertyName]; + }; + } + + currentSpies().push({ + restoreObjectToOriginalState: restoreStrategy + }); + + descriptor[accessType] = spy; + + Object.defineProperty(obj, propertyName, descriptor); + + return spy; + }; + + this.spyOnAllFunctions = function(obj, includeNonEnumerable) { + if (j$.util.isUndefined(obj)) { + throw new Error( + 'spyOnAllFunctions could not find an object to spy upon' + ); + } + + let pointer = obj, + propsToSpyOn = [], + properties, + propertiesToSkip = []; + + while ( + pointer && + (!includeNonEnumerable || pointer !== Object.prototype) + ) { + properties = getProps(pointer, includeNonEnumerable); + properties = properties.filter(function(prop) { + return propertiesToSkip.indexOf(prop) === -1; + }); + propertiesToSkip = propertiesToSkip.concat(properties); + propsToSpyOn = propsToSpyOn.concat( + getSpyableFunctionProps(pointer, properties) + ); + pointer = Object.getPrototypeOf(pointer); + } + + for (const prop of propsToSpyOn) { + this.spyOn(obj, prop); + } + + return obj; + }; + + this.clearSpies = function() { + const spies = currentSpies(); + for (let i = spies.length - 1; i >= 0; i--) { + const spyEntry = spies[i]; + spyEntry.restoreObjectToOriginalState(); + } + }; + } + + function getProps(obj, includeNonEnumerable) { + const enumerableProperties = Object.keys(obj); + + if (!includeNonEnumerable) { + return enumerableProperties; + } + + return Object.getOwnPropertyNames(obj).filter(function(prop) { + return ( + prop !== 'constructor' || + enumerableProperties.indexOf('constructor') > -1 + ); + }); + } + + function getSpyableFunctionProps(obj, propertiesToCheck) { + const props = []; + + for (const prop of propertiesToCheck) { + if ( + Object.prototype.hasOwnProperty.call(obj, prop) && + isSpyableProp(obj, prop) + ) { + props.push(prop); + } + } + return props; + } + + function isSpyableProp(obj, prop) { + let value; + try { + value = obj[prop]; + } catch (e) { + return false; + } + + if (value instanceof Function) { + const descriptor = Object.getOwnPropertyDescriptor(obj, prop); + return (descriptor.writable || descriptor.set) && descriptor.configurable; + } + return false; + } + + return SpyRegistry; +}; + +getJasmineRequireObj().SpyStrategy = function(j$) { + /** + * @interface SpyStrategy + */ + function SpyStrategy(options) { + options = options || {}; + + /** + * Get the identifying information for the spy. + * @name SpyStrategy#identity + * @since 3.0.0 + * @member + * @type {String} + */ + this.identity = options.name || 'unknown'; + this.originalFn = options.fn || function() {}; + this.getSpy = options.getSpy || function() {}; + this.plan = this._defaultPlan = function() {}; + + const cs = options.customStrategies || {}; + for (const k in cs) { + if (j$.util.has(cs, k) && !this[k]) { + this[k] = createCustomPlan(cs[k]); + } + } + + /** + * Tell the spy to return a promise resolving to the specified value when invoked. + * @name SpyStrategy#resolveTo + * @since 3.5.0 + * @function + * @param {*} value The value to return. + */ + this.resolveTo = function(value) { + this.plan = function() { + return Promise.resolve(value); + }; + return this.getSpy(); + }; + + /** + * Tell the spy to return a promise rejecting with the specified value when invoked. + * @name SpyStrategy#rejectWith + * @since 3.5.0 + * @function + * @param {*} value The value to return. + */ + this.rejectWith = function(value) { + this.plan = function() { + return Promise.reject(value); + }; + return this.getSpy(); + }; + } + + function createCustomPlan(factory) { + return function() { + const plan = factory.apply(null, arguments); + + if (!j$.isFunction_(plan)) { + throw new Error('Spy strategy must return a function'); + } + + this.plan = plan; + return this.getSpy(); + }; + } + + /** + * Execute the current spy strategy. + * @name SpyStrategy#exec + * @since 2.0.0 + * @function + */ + SpyStrategy.prototype.exec = function(context, args, invokeNew) { + const contextArgs = [context].concat( + args ? Array.prototype.slice.call(args) : [] + ); + const target = this.plan.bind.apply(this.plan, contextArgs); + + return invokeNew ? new target() : target(); + }; + + /** + * Tell the spy to call through to the real implementation when invoked. + * @name SpyStrategy#callThrough + * @since 2.0.0 + * @function + */ + SpyStrategy.prototype.callThrough = function() { + this.plan = this.originalFn; + return this.getSpy(); + }; + + /** + * Tell the spy to return the value when invoked. + * @name SpyStrategy#returnValue + * @since 2.0.0 + * @function + * @param {*} value The value to return. + */ + SpyStrategy.prototype.returnValue = function(value) { + this.plan = function() { + return value; + }; + return this.getSpy(); + }; + + /** + * Tell the spy to return one of the specified values (sequentially) each time the spy is invoked. + * @name SpyStrategy#returnValues + * @since 2.1.0 + * @function + * @param {...*} values - Values to be returned on subsequent calls to the spy. + */ + SpyStrategy.prototype.returnValues = function() { + const values = Array.prototype.slice.call(arguments); + this.plan = function() { + return values.shift(); + }; + return this.getSpy(); + }; + + /** + * Tell the spy to throw an error when invoked. + * @name SpyStrategy#throwError + * @since 2.0.0 + * @function + * @param {Error|Object|String} something Thing to throw + */ + SpyStrategy.prototype.throwError = function(something) { + const error = j$.isString_(something) ? new Error(something) : something; + this.plan = function() { + throw error; + }; + return this.getSpy(); + }; + + /** + * Tell the spy to call a fake implementation when invoked. + * @name SpyStrategy#callFake + * @since 2.0.0 + * @function + * @param {Function} fn The function to invoke with the passed parameters. + */ + SpyStrategy.prototype.callFake = function(fn) { + if ( + !( + j$.isFunction_(fn) || + j$.isAsyncFunction_(fn) || + j$.isGeneratorFunction_(fn) + ) + ) { + throw new Error( + 'Argument passed to callFake should be a function, got ' + fn + ); + } + this.plan = fn; + return this.getSpy(); + }; + + /** + * Tell the spy to do nothing when invoked. This is the default. + * @name SpyStrategy#stub + * @since 2.0.0 + * @function + */ + SpyStrategy.prototype.stub = function(fn) { + this.plan = function() {}; + return this.getSpy(); + }; + + SpyStrategy.prototype.isConfigured = function() { + return this.plan !== this._defaultPlan; + }; + + return SpyStrategy; +}; + +getJasmineRequireObj().StackTrace = function(j$) { + function StackTrace(error) { + let lines = error.stack.split('\n').filter(function(line) { + return line !== ''; + }); + + const extractResult = extractMessage(error.message, lines); + + if (extractResult) { + this.message = extractResult.message; + lines = extractResult.remainder; + } + + const parseResult = tryParseFrames(lines); + this.frames = parseResult.frames; + this.style = parseResult.style; + } + + const framePatterns = [ + // Node, Chrome, Edge + // e.g. " at QueueRunner.run (http://localhost:8888/__jasmine__/jasmine.js:4320:20)" + // Note that the "function name" can include a surprisingly large set of + // characters, including angle brackets and square brackets. + { + re: /^\s*at ([^\)]+) \(([^\)]+)\)$/, + fnIx: 1, + fileLineColIx: 2, + style: 'v8' + }, + + // NodeJS alternate form, often mixed in with the Chrome style + // e.g. " at /some/path:4320:20 + { re: /\s*at (.+)$/, fileLineColIx: 1, style: 'v8' }, + + // PhantomJS on OS X, Safari, Firefox + // e.g. "run@http://localhost:8888/__jasmine__/jasmine.js:4320:27" + // or "http://localhost:8888/__jasmine__/jasmine.js:4320:27" + { + re: /^(?:(([^@\s]+)@)|@)?([^\s]+)$/, + fnIx: 2, + fileLineColIx: 3, + style: 'webkit' + } + ]; + + // regexes should capture the function name (if any) as group 1 + // and the file, line, and column as group 2. + function tryParseFrames(lines) { + let style = null; + const frames = lines.map(function(line) { + const convertedLine = first(framePatterns, function(pattern) { + const overallMatch = line.match(pattern.re); + if (!overallMatch) { + return null; + } + + const fileLineColMatch = overallMatch[pattern.fileLineColIx].match( + /^(.*):(\d+):\d+$/ + ); + if (!fileLineColMatch) { + return null; + } + + style = style || pattern.style; + return { + raw: line, + file: fileLineColMatch[1], + line: parseInt(fileLineColMatch[2], 10), + func: overallMatch[pattern.fnIx] + }; + }); + + return convertedLine || { raw: line }; + }); + + return { + style: style, + frames: frames + }; + } + + function first(items, fn) { + for (const item of items) { + const result = fn(item); + + if (result) { + return result; + } + } + } + + function extractMessage(message, stackLines) { + const len = messagePrefixLength(message, stackLines); + + if (len > 0) { + return { + message: stackLines.slice(0, len).join('\n'), + remainder: stackLines.slice(len) + }; + } + } + + function messagePrefixLength(message, stackLines) { + if (!stackLines[0].match(/^\w*Error/)) { + return 0; + } + + const messageLines = message.split('\n'); + + for (let i = 1; i < messageLines.length; i++) { + if (messageLines[i] !== stackLines[i]) { + return 0; + } + } + + return messageLines.length; + } + + return StackTrace; +}; + +getJasmineRequireObj().Suite = function(j$) { + function Suite(attrs) { + this.env = attrs.env; + this.id = attrs.id; + this.parentSuite = attrs.parentSuite; + this.description = attrs.description; + this.reportedParentSuiteId = attrs.reportedParentSuiteId; + this.filename = attrs.filename; + this.expectationFactory = attrs.expectationFactory; + this.asyncExpectationFactory = attrs.asyncExpectationFactory; + this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; + this.autoCleanClosures = + attrs.autoCleanClosures === undefined ? true : !!attrs.autoCleanClosures; + this.onLateError = attrs.onLateError || function() {}; + + this.beforeFns = []; + this.afterFns = []; + this.beforeAllFns = []; + this.afterAllFns = []; + this.timer = attrs.timer || new j$.Timer(); + this.children = []; + + this.reset(); + } + + Suite.prototype.setSuiteProperty = function(key, value) { + this.result.properties = this.result.properties || {}; + this.result.properties[key] = value; + }; + + Suite.prototype.getFullName = function() { + const fullName = []; + for ( + let parentSuite = this; + parentSuite; + parentSuite = parentSuite.parentSuite + ) { + if (parentSuite.parentSuite) { + fullName.unshift(parentSuite.description); + } + } + return fullName.join(' '); + }; + + /* + * Mark the suite with "pending" status + */ + Suite.prototype.pend = function() { + this.markedPending = true; + }; + + /* + * Like {@link Suite#pend}, but pending state will survive {@link Spec#reset} + * Useful for fdescribe, xdescribe, where pending state should remain. + */ + Suite.prototype.exclude = function() { + this.pend(); + this.markedExcluding = true; + }; + + Suite.prototype.beforeEach = function(fn) { + this.beforeFns.unshift({ ...fn, suite: this }); + }; + + Suite.prototype.beforeAll = function(fn) { + this.beforeAllFns.push({ ...fn, type: 'beforeAll', suite: this }); + }; + + Suite.prototype.afterEach = function(fn) { + this.afterFns.unshift({ ...fn, suite: this, type: 'afterEach' }); + }; + + Suite.prototype.afterAll = function(fn) { + this.afterAllFns.unshift({ ...fn, type: 'afterAll' }); + }; + + Suite.prototype.startTimer = function() { + this.timer.start(); + }; + + Suite.prototype.endTimer = function() { + this.result.duration = this.timer.elapsed(); + }; + + function removeFns(queueableFns) { + for (const qf of queueableFns) { + qf.fn = null; + } + } + + Suite.prototype.cleanupBeforeAfter = function() { + if (this.autoCleanClosures) { + removeFns(this.beforeAllFns); + removeFns(this.afterAllFns); + removeFns(this.beforeFns); + removeFns(this.afterFns); + } + }; + + Suite.prototype.reset = function() { + /** + * @typedef SuiteResult + * @property {String} id - The unique id of this suite. + * @property {String} description - The description text passed to the {@link describe} that made this suite. + * @property {String} fullName - The full description including all ancestors of this suite. + * @property {String|null} parentSuiteId - The ID of the suite containing this suite, or null if this is not in another describe(). + * @property {String} filename - The name of the file the suite was defined in. + * @property {Expectation[]} failedExpectations - The list of expectations that failed in an {@link afterAll} for this suite. + * @property {Expectation[]} deprecationWarnings - The list of deprecation warnings that occurred on this suite. + * @property {String} status - Once the suite has completed, this string represents the pass/fail status of this suite. + * @property {number} duration - The time in ms for Suite execution, including any before/afterAll, before/afterEach. + * @property {Object} properties - User-supplied properties, if any, that were set using {@link Env#setSuiteProperty} + * @since 2.0.0 + */ + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + parentSuiteId: this.reportedParentSuiteId, + filename: this.filename, + failedExpectations: [], + deprecationWarnings: [], + duration: null, + properties: null + }; + this.markedPending = this.markedExcluding; + this.children.forEach(function(child) { + child.reset(); + }); + this.reportedDone = false; + }; + + Suite.prototype.removeChildren = function() { + this.children = []; + }; + + Suite.prototype.addChild = function(child) { + this.children.push(child); + }; + + Suite.prototype.status = function() { + if (this.markedPending) { + return 'pending'; + } + + if (this.result.failedExpectations.length > 0) { + return 'failed'; + } else { + return 'passed'; + } + }; + + Suite.prototype.canBeReentered = function() { + return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; + }; + + Suite.prototype.getResult = function() { + this.result.status = this.status(); + return this.result; + }; + + Suite.prototype.sharedUserContext = function() { + if (!this.sharedContext) { + this.sharedContext = this.parentSuite + ? this.parentSuite.clonedSharedUserContext() + : new j$.UserContext(); + } + + return this.sharedContext; + }; + + Suite.prototype.clonedSharedUserContext = function() { + return j$.UserContext.fromExisting(this.sharedUserContext()); + }; + + Suite.prototype.handleException = function() { + if (arguments[0] instanceof j$.errors.ExpectationFailed) { + return; + } + + const data = { + matcherName: '', + passed: false, + expected: '', + actual: '', + error: arguments[0] + }; + const failedExpectation = j$.buildExpectationResult(data); + + if (!this.parentSuite) { + failedExpectation.globalErrorType = 'afterAll'; + } + + if (this.reportedDone) { + this.onLateError(failedExpectation); + } else { + this.result.failedExpectations.push(failedExpectation); + } + }; + + Suite.prototype.onMultipleDone = function() { + let msg; + + // Issue a deprecation. Include the context ourselves and pass + // ignoreRunnable: true, since getting here always means that we've already + // moved on and the current runnable isn't the one that caused the problem. + if (this.parentSuite) { + msg = + "An asynchronous beforeAll or afterAll function called its 'done' " + + 'callback more than once.\n' + + '(in suite: ' + + this.getFullName() + + ')'; + } else { + msg = + 'A top-level beforeAll or afterAll function called its ' + + "'done' callback more than once."; + } + + this.onLateError(new Error(msg)); + }; + + Suite.prototype.addExpectationResult = function() { + if (isFailure(arguments)) { + const data = arguments[1]; + const expectationResult = j$.buildExpectationResult(data); + + if (this.reportedDone) { + this.onLateError(expectationResult); + } else { + this.result.failedExpectations.push(expectationResult); + + // TODO: refactor so that we don't need to override cached status + if (this.result.status) { + this.result.status = 'failed'; + } + } + + if (this.throwOnExpectationFailure) { + throw new j$.errors.ExpectationFailed(); + } + } + }; + + Suite.prototype.addDeprecationWarning = function(deprecation) { + if (typeof deprecation === 'string') { + deprecation = { message: deprecation }; + } + this.result.deprecationWarnings.push( + j$.buildExpectationResult(deprecation) + ); + }; + + Object.defineProperty(Suite.prototype, 'metadata', { + get: function() { + if (!this.metadata_) { + this.metadata_ = new SuiteMetadata(this); + } + + return this.metadata_; + } + }); + + /** + * @interface Suite + * @see Env#topSuite + * @since 2.0.0 + */ + function SuiteMetadata(suite) { + this.suite_ = suite; + /** + * The unique ID of this suite. + * @name Suite#id + * @readonly + * @type {string} + * @since 2.0.0 + */ + this.id = suite.id; + + /** + * The parent of this suite, or null if this is the top suite. + * @name Suite#parentSuite + * @readonly + * @type {Suite} + */ + this.parentSuite = suite.parentSuite ? suite.parentSuite.metadata : null; + + /** + * The description passed to the {@link describe} that created this suite. + * @name Suite#description + * @readonly + * @type {string} + * @since 2.0.0 + */ + this.description = suite.description; + } + + /** + * The full description including all ancestors of this suite. + * @name Suite#getFullName + * @function + * @returns {string} + * @since 2.0.0 + */ + SuiteMetadata.prototype.getFullName = function() { + return this.suite_.getFullName(); + }; + + /** + * The suite's children. + * @name Suite#children + * @type {Array.<(Spec|Suite)>} + * @since 2.0.0 + */ + Object.defineProperty(SuiteMetadata.prototype, 'children', { + get: function() { + return this.suite_.children.map(child => child.metadata); + } + }); + + function isFailure(args) { + return !args[0]; + } + + return Suite; +}; + +getJasmineRequireObj().SuiteBuilder = function(j$) { + class SuiteBuilder { + constructor(options) { + this.env_ = options.env; + this.expectationFactory_ = options.expectationFactory; + this.suiteAsyncExpectationFactory_ = function(actual, suite) { + return options.asyncExpectationFactory(actual, suite, 'Suite'); + }; + this.specAsyncExpectationFactory_ = function(actual, suite) { + return options.asyncExpectationFactory(actual, suite, 'Spec'); + }; + this.onLateError_ = options.onLateError; + this.specResultCallback_ = options.specResultCallback; + this.specStarted_ = options.specStarted; + + this.nextSuiteId_ = 0; + this.nextSpecId_ = 0; + + this.topSuite = this.suiteFactory_('Jasmine__TopLevel__Suite'); + this.currentDeclarationSuite_ = this.topSuite; + this.totalSpecsDefined = 0; + this.focusedRunables = []; + } + + inDescribe() { + return this.currentDeclarationSuite_ !== this.topSuite; + } + + parallelReset() { + this.topSuite.removeChildren(); + this.topSuite.reset(); + this.totalSpecsDefined = 0; + this.focusedRunables = []; + } + + describe(description, definitionFn, filename) { + ensureIsFunction(definitionFn, 'describe'); + const suite = this.suiteFactory_(description, filename); + if (definitionFn.length > 0) { + throw new Error('describe does not expect any arguments'); + } + if (this.currentDeclarationSuite_.markedExcluding) { + suite.exclude(); + } + this.addSpecsToSuite_(suite, definitionFn); + return suite; + } + + fdescribe(description, definitionFn, filename) { + ensureIsFunction(definitionFn, 'fdescribe'); + const suite = this.suiteFactory_(description, filename); + suite.isFocused = true; + + this.focusedRunables.push(suite.id); + this.unfocusAncestor_(); + this.addSpecsToSuite_(suite, definitionFn); + + return suite; + } + + xdescribe(description, definitionFn, filename) { + ensureIsFunction(definitionFn, 'xdescribe'); + const suite = this.suiteFactory_(description, filename); + suite.exclude(); + this.addSpecsToSuite_(suite, definitionFn); + + return suite; + } + + it(description, fn, timeout, filename) { + // it() sometimes doesn't have a fn argument, so only check the type if + // it's given. + if (arguments.length > 1 && typeof fn !== 'undefined') { + ensureIsFunctionOrAsync(fn, 'it'); + } + + return this.it_(description, fn, timeout, filename); + } + + xit(description, fn, timeout, filename) { + // xit(), like it(), doesn't always have a fn argument, so only check the + // type when needed. + if (arguments.length > 1 && typeof fn !== 'undefined') { + ensureIsFunctionOrAsync(fn, 'xit'); + } + const spec = this.it_(description, fn, timeout, filename); + spec.exclude('Temporarily disabled with xit'); + return spec; + } + + fit(description, fn, timeout, filename) { + // Unlike it and xit, the function is required because it doesn't make + // sense to focus on nothing. + ensureIsFunctionOrAsync(fn, 'fit'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + const spec = this.specFactory_(description, fn, timeout, filename); + this.currentDeclarationSuite_.addChild(spec); + this.focusedRunables.push(spec.id); + this.unfocusAncestor_(); + return spec; + } + + beforeEach(beforeEachFunction, timeout) { + ensureIsFunctionOrAsync(beforeEachFunction, 'beforeEach'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + this.currentDeclarationSuite_.beforeEach({ + fn: beforeEachFunction, + timeout: timeout || 0 + }); + } + + beforeAll(beforeAllFunction, timeout) { + ensureIsFunctionOrAsync(beforeAllFunction, 'beforeAll'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + this.currentDeclarationSuite_.beforeAll({ + fn: beforeAllFunction, + timeout: timeout || 0 + }); + } + + afterEach(afterEachFunction, timeout) { + ensureIsFunctionOrAsync(afterEachFunction, 'afterEach'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + afterEachFunction.isCleanup = true; + this.currentDeclarationSuite_.afterEach({ + fn: afterEachFunction, + timeout: timeout || 0 + }); + } + + afterAll(afterAllFunction, timeout) { + ensureIsFunctionOrAsync(afterAllFunction, 'afterAll'); + + if (timeout) { + j$.util.validateTimeout(timeout); + } + + this.currentDeclarationSuite_.afterAll({ + fn: afterAllFunction, + timeout: timeout || 0 + }); + } + + it_(description, fn, timeout, filename) { + if (timeout) { + j$.util.validateTimeout(timeout); + } + + const spec = this.specFactory_(description, fn, timeout, filename); + if (this.currentDeclarationSuite_.markedExcluding) { + spec.exclude(); + } + this.currentDeclarationSuite_.addChild(spec); + + return spec; + } + + suiteFactory_(description, filename) { + const config = this.env_.configuration(); + const parentSuite = this.currentDeclarationSuite_; + const reportedParentSuiteId = + parentSuite === this.topSuite ? null : parentSuite.id; + return new j$.Suite({ + id: 'suite' + this.nextSuiteId_++, + description, + filename, + parentSuite, + reportedParentSuiteId, + timer: new j$.Timer(), + expectationFactory: this.expectationFactory_, + asyncExpectationFactory: this.suiteAsyncExpectationFactory_, + throwOnExpectationFailure: config.stopSpecOnExpectationFailure, + autoCleanClosures: config.autoCleanClosures, + onLateError: this.onLateError_ + }); + } + + addSpecsToSuite_(suite, definitionFn) { + const parentSuite = this.currentDeclarationSuite_; + parentSuite.addChild(suite); + this.currentDeclarationSuite_ = suite; + let threw = false; + + try { + definitionFn(); + } catch (e) { + suite.handleException(e); + threw = true; + } + + if (suite.parentSuite && !suite.children.length && !threw) { + throw new Error( + `describe with no children (describe() or it()): ${suite.getFullName()}` + ); + } + + this.currentDeclarationSuite_ = parentSuite; + } + + specFactory_(description, fn, timeout, filename) { + this.totalSpecsDefined++; + const config = this.env_.configuration(); + const suite = this.currentDeclarationSuite_; + const parentSuiteId = suite === this.topSuite ? null : suite.id; + const spec = new j$.Spec({ + id: 'spec' + this.nextSpecId_++, + filename, + parentSuiteId, + beforeAndAfterFns: beforeAndAfterFns(suite), + expectationFactory: this.expectationFactory_, + asyncExpectationFactory: this.specAsyncExpectationFactory_, + onLateError: this.onLateError_, + resultCallback: (result, next) => { + this.specResultCallback_(spec, result, next); + }, + getSpecName: function(spec) { + return getSpecName(spec, suite); + }, + onStart: (spec, next) => this.specStarted_(spec, suite, next), + description: description, + userContext: function() { + return suite.clonedSharedUserContext(); + }, + queueableFn: { + fn: fn, + timeout: timeout || 0 + }, + throwOnExpectationFailure: config.stopSpecOnExpectationFailure, + autoCleanClosures: config.autoCleanClosures, + timer: new j$.Timer() + }); + return spec; + } + + unfocusAncestor_() { + const focusedAncestor = findFocusedAncestor( + this.currentDeclarationSuite_ + ); + + if (focusedAncestor) { + for (let i = 0; i < this.focusedRunables.length; i++) { + if (this.focusedRunables[i] === focusedAncestor) { + this.focusedRunables.splice(i, 1); + break; + } + } + } + } + } + + function findFocusedAncestor(suite) { + while (suite) { + if (suite.isFocused) { + return suite.id; + } + suite = suite.parentSuite; + } + + return null; + } + + function ensureIsFunction(fn, caller) { + if (!j$.isFunction_(fn)) { + throw new Error( + caller + ' expects a function argument; received ' + j$.getType_(fn) + ); + } + } + + function ensureIsFunctionOrAsync(fn, caller) { + if (!j$.isFunction_(fn) && !j$.isAsyncFunction_(fn)) { + throw new Error( + caller + ' expects a function argument; received ' + j$.getType_(fn) + ); + } + } + + function beforeAndAfterFns(targetSuite) { + return function() { + let befores = [], + afters = [], + suite = targetSuite; + + while (suite) { + befores = befores.concat(suite.beforeFns); + afters = afters.concat(suite.afterFns); + + suite = suite.parentSuite; + } + + return { + befores: befores.reverse(), + afters: afters + }; + }; + } + + function getSpecName(spec, suite) { + const fullName = [spec.description], + suiteFullName = suite.getFullName(); + + if (suiteFullName !== '') { + fullName.unshift(suiteFullName); + } + return fullName.join(' '); + } + + return SuiteBuilder; +}; + +getJasmineRequireObj().Timer = function() { + const defaultNow = (function(Date) { + return function() { + return new Date().getTime(); + }; + })(Date); + + /** + * @class Timer + * @classdesc Tracks elapsed time + * @example + * const timer = new jasmine.Timer(); + * timer.start(); + * const elapsed = timer.elapsed() + */ + function Timer(options) { + options = options || {}; + + const now = options.now || defaultNow; + let startTime; + + /** + * Starts the timer. + * @function + * @name Timer#start + */ + this.start = function() { + startTime = now(); + }; + + /** + * Determines the time since the timer was started. + * @function + * @name Timer#elapsed + * @returns {number} Elapsed time in milliseconds, or NaN if the timer has not been started + */ + this.elapsed = function() { + return now() - startTime; + }; + } + + return Timer; +}; + +getJasmineRequireObj().TreeProcessor = function() { + function TreeProcessor(attrs) { + const tree = attrs.tree; + const runnableIds = attrs.runnableIds; + const queueRunnerFactory = attrs.queueRunnerFactory; + const nodeStart = attrs.nodeStart || function() {}; + const nodeComplete = attrs.nodeComplete || function() {}; + const failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations; + const orderChildren = + attrs.orderChildren || + function(node) { + return node.children; + }; + const excludeNode = + attrs.excludeNode || + function(node) { + return false; + }; + let stats = { valid: true }; + let processed = false; + const defaultMin = Infinity; + const defaultMax = 1 - Infinity; + + this.processTree = function() { + processNode(tree, true); + processed = true; + return stats; + }; + + this.execute = async function() { + if (!processed) { + this.processTree(); + } + + if (!stats.valid) { + throw 'invalid order'; + } + + const childFns = wrapChildren(tree, 0); + + await new Promise(function(resolve) { + queueRunnerFactory({ + queueableFns: childFns, + userContext: tree.sharedUserContext(), + onException: function() { + tree.handleException.apply(tree, arguments); + }, + onComplete: resolve, + onMultipleDone: tree.onMultipleDone + ? tree.onMultipleDone.bind(tree) + : null + }); + }); + }; + + function runnableIndex(id) { + for (let i = 0; i < runnableIds.length; i++) { + if (runnableIds[i] === id) { + return i; + } + } + } + + function processNode(node, parentExcluded) { + const executableIndex = runnableIndex(node.id); + + if (executableIndex !== undefined) { + parentExcluded = false; + } + + if (!node.children) { + const excluded = parentExcluded || excludeNode(node); + stats[node.id] = { + excluded: excluded, + willExecute: !excluded && !node.markedPending, + segments: [ + { + index: 0, + owner: node, + nodes: [node], + min: startingMin(executableIndex), + max: startingMax(executableIndex) + } + ] + }; + } else { + let hasExecutableChild = false; + + const orderedChildren = orderChildren(node); + + for (let i = 0; i < orderedChildren.length; i++) { + const child = orderedChildren[i]; + + processNode(child, parentExcluded); + + if (!stats.valid) { + return; + } + + const childStats = stats[child.id]; + + hasExecutableChild = hasExecutableChild || childStats.willExecute; + } + + stats[node.id] = { + excluded: parentExcluded, + willExecute: hasExecutableChild + }; + + segmentChildren(node, orderedChildren, stats[node.id], executableIndex); + + if (!node.canBeReentered() && stats[node.id].segments.length > 1) { + stats = { valid: false }; + } + } + } + + function startingMin(executableIndex) { + return executableIndex === undefined ? defaultMin : executableIndex; + } + + function startingMax(executableIndex) { + return executableIndex === undefined ? defaultMax : executableIndex; + } + + function segmentChildren( + node, + orderedChildren, + nodeStats, + executableIndex + ) { + let currentSegment = { + index: 0, + owner: node, + nodes: [], + min: startingMin(executableIndex), + max: startingMax(executableIndex) + }, + result = [currentSegment], + lastMax = defaultMax, + orderedChildSegments = orderChildSegments(orderedChildren); + + function isSegmentBoundary(minIndex) { + return ( + lastMax !== defaultMax && + minIndex !== defaultMin && + lastMax < minIndex - 1 + ); + } + + for (let i = 0; i < orderedChildSegments.length; i++) { + const childSegment = orderedChildSegments[i], + maxIndex = childSegment.max, + minIndex = childSegment.min; + + if (isSegmentBoundary(minIndex)) { + currentSegment = { + index: result.length, + owner: node, + nodes: [], + min: defaultMin, + max: defaultMax + }; + result.push(currentSegment); + } + + currentSegment.nodes.push(childSegment); + currentSegment.min = Math.min(currentSegment.min, minIndex); + currentSegment.max = Math.max(currentSegment.max, maxIndex); + lastMax = maxIndex; + } + + nodeStats.segments = result; + } + + function orderChildSegments(children) { + const specifiedOrder = [], + unspecifiedOrder = []; + + for (let i = 0; i < children.length; i++) { + const child = children[i], + segments = stats[child.id].segments; + + for (let j = 0; j < segments.length; j++) { + const seg = segments[j]; + + if (seg.min === defaultMin) { + unspecifiedOrder.push(seg); + } else { + specifiedOrder.push(seg); + } + } + } + + specifiedOrder.sort(function(a, b) { + return a.min - b.min; + }); + + return specifiedOrder.concat(unspecifiedOrder); + } + + function executeNode(node, segmentNumber) { + if (node.children) { + return { + fn: function(done) { + const onStart = { + fn: function(next) { + nodeStart(node, next); + } + }; + + queueRunnerFactory({ + onComplete: function() { + const args = Array.prototype.slice.call(arguments, [0]); + node.cleanupBeforeAfter(); + nodeComplete(node, node.getResult(), function() { + done.apply(undefined, args); + }); + }, + queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)), + userContext: node.sharedUserContext(), + onException: function() { + node.handleException.apply(node, arguments); + }, + onMultipleDone: node.onMultipleDone + ? node.onMultipleDone.bind(node) + : null + }); + } + }; + } else { + return { + fn: function(done) { + node.execute( + queueRunnerFactory, + done, + stats[node.id].excluded, + failSpecWithNoExpectations + ); + } + }; + } + } + + function wrapChildren(node, segmentNumber) { + const result = [], + segmentChildren = stats[node.id].segments[segmentNumber].nodes; + + for (let i = 0; i < segmentChildren.length; i++) { + result.push( + executeNode(segmentChildren[i].owner, segmentChildren[i].index) + ); + } + + if (!stats[node.id].willExecute) { + return result; + } + + return node.beforeAllFns.concat(result).concat(node.afterAllFns); + } + } + + return TreeProcessor; +}; + +getJasmineRequireObj().UserContext = function(j$) { + function UserContext() {} + + UserContext.fromExisting = function(oldContext) { + const context = new UserContext(); + + for (const prop in oldContext) { + if (oldContext.hasOwnProperty(prop)) { + context[prop] = oldContext[prop]; + } + } + + return context; + }; + + return UserContext; +}; + +getJasmineRequireObj().version = function() { + return '5.1.2'; +}; diff --git a/tests/js/lib/jasmine-5.1.2/jasmine_favicon.png b/tests/js/lib/jasmine-5.1.2/jasmine_favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..3b84583be4b9d5ae9cd5cae07b2dbaa5ebb0ad1c GIT binary patch literal 1486 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabRA=0VDb;}332@o1PuQh8X8uGu4-^- zn3*=SA+%wV=cI;&hMB$%-Lc(MLmN8%IwwUpbcA;F2Q;)twND9bn-tpC9oR4-vb8I; zp+Bg#FSKP+P(yD-%f!%zo}iZgh=%Ua=KkP@iNVbiLYsSn8~VeW`+}P$1~&ACHcbd_ z=nZb_32o{NZkZ6&1k^hrxT!a&r8lIdAIJ@9nh?|iRMsET+#A%`AKKg-(%2K)*cZ|~ zA-J&*$PEUH08MM`4Q=iVY3vVfhN$TUscGs5sR8P31X|G_+SnTcv<0XVC=Rr!u|K4# zHyCJU6UYRR;%2BtKv^I=q#2|DECn_k@wXU|=@ zeD&J(8#iy=zH|5fgNKiwJbm`!<*V0k-oF3v@zdvTKYsrD^Y>rGdNDU(R>|^oaSW-5 z%f0Y2x+hSk{p0&HA;E60Oq1S3r7U;(X3MH#?W9nxXzk)srlC4P;cZYe&v*G}mgi%< ze(bTl{@&_)cX8b-kvX%ff9D22e*M~7edm|;|Jb8m2JBzkzKiSBKR0n*?XSyM6fxZY zJ4bb;vGKRiKP(ztq3sJx9iq$Re`-EEQU0KE$sdyqayzp6H-7x+oGKZ_6;hQ_Y|p&y z7o`N7g@ z8Yl1HKL;mFo4BdxljF1}KbfMR6nNdLyPnEap1MqBdI?{g$L58$P8+wftLX5R70)7n`#pwItnbma$w{%`M(`wpsu2rJ60rdGCc> z+~lm)viMYM`=uXwK_$`#_UtRZMO=2-IV)l2vc~)doyPvg-RmxtACQ#v{7}5$&isV+ zPws~n?@yNGy#H1=vTI?;CAl9~9#VgqJ+7`4y~R+Wyj0U+){?lm9r6mtf0gl_VmUW^ z%eje%?jM%^eLuUg#ybXaQM{yQy&8UgiQ`HQ+=iTpjfd0iJ@ ssyFi&dF4%9dEry;pNN)Q>$jg_dr3r;PHA*CFc&d+y85}Sb4q9e0J812W&i*H literal 0 HcmV?d00001 diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 0000000..9dc9c3d --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,4655 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from flask import json +from plantuml_gui.activity import activity_indices +from plantuml_gui.app import polychunktotext, svgtochunklistpolygon +from plantuml_gui.classes import PolyElement +from plantuml_gui.if_statements import ( + build_tree, + find_start, + findelsebounds, + findifbounds, +) + + +class TestRender: + def test_render(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +@enduml""" + } + with client: + response = client.post( + "/render", + data=json.dumps(test_data), + content_type="application/json", + ) + assert """" + ) + svgchunklist = svgtochunklistpolygon( + """Activity 1StatementHelloyesnoActivityActivity""" + ) + output = ["Statement\nHello", "yes", "no"] + assert ( + polychunktotext( + """@startuml + :Activity 1; + if (Statement + Hello) then (yes) + :Activity; + else (no) + :Activity; + endif + @enduml""", + svgchunklist, + clickedsvg, + ) + == output + ) + + def test_polytotextbothbranchempty(self): + clickedsvg = PolyElement.from_svg( + """""" + ) + svgchunklist = svgtochunklistpolygon( + """Activity 1Statementyesno""" + ) + output = ["Statement", "yes", "no"] + assert ( + polychunktotext( + """@startuml +:Activity 1; +if (Statement) then (yes) +else (no) +endif +@enduml""", + svgchunklist, + clickedsvg, + ) + == output + ) + + def test_link_in_statement(self): + clickedsvg = PolyElement.from_svg( + """""" + ) + svgchunklist = svgtochunklistpolygon( + """Activity 1hejgoogledåyesnoActivityActivity""" + ) + output = ["hej [[google.com google]] då", "yes", "no"] + assert ( + polychunktotext( + """@startuml +:Activity 1; +if (hej [[google.com google]] då) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""", + svgchunklist, + clickedsvg, + ) + == output + ) + + def test_polytotextbranch1empty(self): + clickedsvg = PolyElement.from_svg( + """""" + ) + svgchunklist = svgtochunklistpolygon( + """Activity 1ActivitynoStatementyes""" + ) + output = ["Statement", "yes", "no"] + assert ( + polychunktotext( + """@startuml +:Activity 1; +if (Statement) then (yes) +else (no) + :Activity; +endif +@enduml""", + svgchunklist, + clickedsvg, + ) + == output + ) + + def test_polytotextbranch2empty(self): + clickedsvg = PolyElement.from_svg( + """""" + ) + svgchunklist = svgtochunklistpolygon( + """Activity 1ActivityyesStatementno""" + ) + output = ["Statement", "yes", "no"] + assert ( + polychunktotext( + """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +else (no) +endif +@enduml""", + svgchunklist, + clickedsvg, + ) + == output + ) + + def test_findelseindex(self): + if_start = 4 + lines = """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +if (Statement) then (yes) + :Activity; +else (noasdasd +asdfasdf +asdfasdf) + :Activity; +endif +else (no) + :Activity; +endif +@enduml""".splitlines() + output = 6, 8 + assert findelsebounds(lines, if_start) == output + + def test_findifindex(self): + start = 4 + lines = """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +if (Statement +asdf) then (yes) + :Activity; +else (no) + :Activity; +endif +else (no) + :Activity; +endif +@enduml""".splitlines() + output = 4, 5 + assert findifbounds(lines, start) == output + + +class TestAppRoutesMerge: + def test_addtomergenoifmerge3(self, client): + test_data = { + "plantuml": """@startuml +start +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +detach +repeat +:Activity; +backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """StatementyesnoActivityActivityActivitynowhile ?yesActivityActivity""", + "svgelement": """""", + "type": "activity", + } + with client: + response = client.post( + "/addToMerge", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +detach +repeat +:Activity; +:Activity; +backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_addtomergenoifmerge2(self, client): + test_data = { + "plantuml": """@startuml +start +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +detach +endif +repeat + :Activity; + backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """StatementyesnoActivityActivityActivitynowhile ?yesActivityActivity""", + "svgelement": """""", + "type": "activity", + } + with client: + response = client.post( + "/addToMerge", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +detach +endif +repeat +:Activity; + :Activity; + backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_addtomergenoifmerge(self, client): + test_data = { + "plantuml": """@startuml +start +if (Statement) then (yes) + :Activity; +detach +else (no) + :Activity; +endif +repeat + :Activity; + backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """StatementyesnoActivityActivityActivitynowhile ?yesActivityActivity""", + "svgelement": """""", + "type": "activity", + } + with client: + response = client.post( + "/addToMerge", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +if (Statement) then (yes) + :Activity; +detach +else (no) + :Activity; +endif +repeat +:Activity; + :Activity; + backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_get_merge_line(self, client): + test_data = { + "plantuml": """@startuml +start +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +repeat + :Activity; + :Activity; + backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """StatementyesnoActivityActivityActivityActivitynowhile ?yesActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/getMergeLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_puml = 6 + + # Assert the result value is as expected + assert result_value == expected_puml + + def test_addtomerge2(self, client): + test_data = { + "plantuml": """@startuml +start +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +repeat + :Activity; + :Activity; + backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """StatementyesnoActivityActivityActivityActivitynowhile ?yesActivityActivity""", + "svgelement": """""", + "type": "activity", + } + with client: + response = client.post( + "/addToMerge", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +:Activity; +repeat + :Activity; + :Activity; + backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_addactivitymerge(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +if (Statement) then (yes) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +:Activity; +endif +:Activity; +else (no) +if (Statement) then (yes) + :Activity; +else (no) + :Actaivity; +endif +:Activity; +endif +:Activity; +@enduml""", + "svg": """ActivityStatementyesnoActivityStatementyesnoActivityActivityStatementyesnoActivityActivityActivityActivityStatementyesnoActivityActaivityActivityActivity""", + "svgelement": """""", + "type": "activity", + } + with client: + response = client.post( + "/addToMerge", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +:Activity; +if (Statement) then (yes) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +:Activity; +endif +:Activity; +else (no) +if (Statement) then (yes) + :Activity; +else (no) + :Actaivity; +endif +:Activity; +:Activity; +endif +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + +class TestAppRoutesGroup: + def test_gettextpartition(self, client): + test_data = { + "plantuml": """@startuml +partition hej { +:Activity; +} +@enduml""", + "svg": """hejActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/getGroupText", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = "hej" + assert response.data.decode("utf-8") == expected_result + + def test_editpartition(self, client): + test_data = { + "plantuml": """@startuml +partition hej { +:Activity; +} +@enduml""", + "svg": """hejActivity""", + "svgelement": """""", + "text": "bom", + } + with client: + response = client.post( + "/editGroup", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +partition bom { +:Activity; +} +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_get_group_line(self, client): + test_data = { + "plantuml": """@startuml +group group +:Activity 1; +end group +partition bom { +:Activity 8; +} +@enduml""", + "svg": """groupActivity 1bomActivity 8""", + "svgelement": """""", + } + with client: + response = client.post( + "/getGroupLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_puml = [1, 3] + + # Assert the result value is as expected + assert result_value == expected_puml + + def test_deletegroupwithpartition(self, client): + test_data = { + "plantuml": """@startuml +group group +:Activity 1; +end group +partition bom { +:Activity 8; +} +@enduml""", + "svg": """groupActivity 1bomActivity 8""", + "svgelement": """""", + } + with client: + response = client.post( + "/deleteGroup", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +:Activity 1; +partition bom { +:Activity 8; +} +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_deletegroup(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +note right +note +end note +group group +if (Statement) then (yes) + :Activity; +else (no) +group hej + :Activity; +end group +endif +end group +stop +@enduml""", + "svg": """noteActivitygroupStatementyesnoActivityhejActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/deleteGroup", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +:Activity; +note right +note +end note +if (Statement) then (yes) + :Activity; +else (no) +group hej + :Activity; +end group +endif +stop +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_editgroupempty(self, client): + test_data = { + "plantuml": """@startuml +start +group group +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +end group +stop +@enduml""", + "svg": """groupStatementyesnoActivityActivity""", + "svgelement": """""", + "text": "", + } + with client: + response = client.post( + "/editGroup", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +stop +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_editgroup(self, client): + test_data = { + "plantuml": """@startuml +start +group group +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +end group +stop +@enduml""", + "svg": """groupStatementyesnoActivityActivity""", + "svgelement": """""", + "text": "hej", + } + with client: + response = client.post( + "/editGroup", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +group hej +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +end group +stop +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_gettextgroup(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +note right +note +end note +group hello +if (Statement) then (yes) +group hej + :Activity; +end group +else (no) + :Activity; +endif +end group +fork + :action; +note left +note +end note +fork again + :action; +end fork +stop +@enduml""", + "svg": """noteActivityhelloStatementyesnohejActivityActivitynoteactionaction""", + "svgelement": """""", + } + with client: + response = client.post( + "/getGroupText", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = "hej" + assert response.data.decode("utf-8") == expected_result + + +class TestAppRouteEncodeDecode: + def test_decode(self, client): + test_data = {"hash": """SoWkIImgAStDuG8pk1nIyrA0F00="""} + with client: + response = client.post( + "/decode", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_encode(self, client): + test_data = { + "plantuml": """@startuml +start +@enduml""" + } + with client: + response = client.post( + "/encode", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """SoWkIImgAStDuG8pk1nIyrA0F00=""" + assert response.data.decode("utf-8") == expected_result + + +class TestAppRoutesWhile: + def test_addforkbreak(self, client): + test_data = { + "plantuml": """@startuml +while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +:hello again; +stop +@enduml""", + "svg": """helloYesST_ONGOINGNohello again""", + "svgelement": """""", + "type": "fork", + "where": "break", + } + with client: + response = client.post( + "/addToWhile", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +fork +:action; +fork again +:action; +end fork +:hello again; +stop +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_addifloop(self, client): + test_data = { + "plantuml": """@startuml +while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +:hello again; +stop +@enduml""", + "svg": """helloYesST_ONGOINGNohello again""", + "svgelement": """""", + "type": "if", + "where": "loop", + } + with client: + response = client.post( + "/addToWhile", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +while (ST_ONGOING) is (Yes) + :hello; +if (Statement) then (yes) +:Activity; +else (no) +:Activity; +endif +endwhile (No); +:hello again; +stop +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_delwhile(self, client): + test_data = { + "plantuml": """@startuml +while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +:hello again; +stop +@enduml""", + "svg": """helloYesST_ONGOINGNohello again""", + "svgelement": """""", + } + with client: + response = client.post( + "/delWhile", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +:hello again; +stop +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_get_while_line(self, client): + test_data = { + "plantuml": """@startuml +while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +:hello again; +stop +@enduml""", + "svg": """helloYesST_ONGOINGNohello again""", + "svgelement": """""", + } + with client: + response = client.post( + "/getWhileLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_puml = [1, 3] + + # Assert the result value is as expected + assert result_value == expected_puml + + def test_editwhilemultiplenests(self, client): + test_data = { + "plantuml": """@startuml +start +while (ST_ONGOING) is (Yes) + :hello; + while (ST_ONGOING) is (Yes) + :hello; + while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +:activity; +endwhile (No); +:activity; +endwhile (No); +:activity; +while (ST_ONGOING) is (Yes) + :hello; + while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +:activity; +endwhile (No); +:activity; +@enduml""", + "svg": """hellohellohelloYesST_ONGOINGNoactivityYesST_ONGOINGNoactivityYesST_ONGOINGNoactivityhellohelloYesST_ONGOINGNoactivityYesST_ONGOINGNoactivity""", + "whilestatement": "A", + "break": "B", + "loop": "C", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextWhile", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +while (ST_ONGOING) is (Yes) + :hello; + while (ST_ONGOING) is (Yes) + :hello; + while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +:activity; +endwhile (No); +:activity; +endwhile (No); +:activity; +while (ST_ONGOING) is (Yes) + :hello; + while (A) is (C) + :hello; +endwhile (B); +:activity; +endwhile (No); +:activity; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_editwhilenested(self, client): + test_data = { + "plantuml": """@startuml +while (ST_ONGOING) is (Yes) + :hello; + while (Hej) is (Bom) + :hello; +endwhile (Bam); +:hello again; +endwhile (No); +:hello again; +stop +@enduml""", + "svg": """hellohelloBomHejBamhello againYesST_ONGOINGNohello again""", + "whilestatement": "A", + "break": "B", + "loop": "C", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextWhile", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +while (ST_ONGOING) is (Yes) + :hello; + while (A) is (C) + :hello; +endwhile (B); +:hello again; +endwhile (No); +:hello again; +stop +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_editwhile(self, client): + test_data = { + "plantuml": """@startuml +while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +:hello again; +stop +@enduml""", + "svg": """helloYesST_ONGOINGNohello again""", + "whilestatement": "Hej", + "break": "Bom", + "loop": "Bam", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextWhile", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +while (Hej) is (Bam) + :hello; +endwhile (Bom); +:hello again; +stop +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_getwhiletext(self, client): + test_data = { + "svg": """ActivityStatementyesnoActivityActivityhelloYesST_ONGOINGNohello again""", + "svgelement": """""", + } + with client: + response = client.post( + "/getTextWhile", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = ["ST_ONGOING", "No", "Yes"] + assert json.loads(response.data.decode("utf-8")) == expected_result + + +class TestAppRouteArrow: + def test_case_duplicate_check(self, client): + test_data = { + "plantuml": r"""@startuml +start +switch (test?) +case ( condition 1) + :Activity; +case ( condition 2) + :Activity; +endswitch +@enduml""", + "svg": """test?ActivityActivitycondition 1condition 2""", + "svgelement": """""", + } + with client: + response = client.post( + "/checkDuplicateArrow", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + assert not result_value + + def test_check_arrow_duplicate(self, client): + test_data = { + "plantuml": r"""@startuml +start +-> Nsmf_PDUSession_CreateSMContext Request +received from AMF; +: 1. Nsmf_PDUSession_CreateSMContext Request; +-> label on arrow; +: 2. activity2; +@enduml""", + "svg": """1. Nsmf_PDUSession_CreateSMContext Request2. activity2Nsmf_PDUSession_CreateSMContext Requestreceived from AMFlabel on arrow""", + "svgelement": """""", + } + with client: + response = client.post( + "/checkDuplicateArrow", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + assert not result_value + + def test_get_arrow_line(self, client): + test_data = { + "plantuml": r"""@startuml +start +-> Nsmf_PDUSession_CreateSMContext Request +received from AMF; +: 1. Nsmf_PDUSession_CreateSMContext Request; +-> label on arrow; +: 2. activity2; +@enduml""", + "svg": """1. Nsmf_PDUSession_CreateSMContext Request2. activity2Nsmf_PDUSession_CreateSMContext Requestreceived from AMFlabel on arrow""", + "svgelement": """""", + } + with client: + response = client.post( + "/getArrowLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_puml = [5, 5] + + # Assert the result value is as expected + assert result_value == expected_puml + + def test_edit_switch_case_multiline(self, client): + test_data = { + "plantuml": r"""@startuml +start +switch (test?) +case (condition\nnewline) + :Activity; +case (hello) + :Activity; +endswitch +@enduml""", + "svg": """test?ActivityActivityconditionnewlinehello""", + "svgelement": """""", + "text": "hello", + } + with client: + response = client.post( + "/editArrow", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = r"""@startuml +start +switch (test?) +case (hello) + :Activity; +case (hello) + :Activity; +endswitch +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_delete_switch_case_nestedswitch(self, client): + test_data = { + "plantuml": r"""@startuml +start +switch (test?) +case ( condition 1) + :Activity; + switch (test?) + case ( condition 1) + :Activity; + case ( condition 2) + :Activity; + endswitch +case ( condition 2) + :Activity; +endswitch +@enduml""", + "svg": """test?Activitytest?ActivityActivityActivitycondition 1condition 2condition 1condition 2""", + "svgelement": """""", + } + with client: + response = client.post( + "/delArrow", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = r"""@startuml +start +switch (test?) +case ( condition 2) + :Activity; +endswitch +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_delete_switch_case(self, client): + test_data = { + "plantuml": r"""@startuml +start +switch (test?) +case ( condition 1) + :Activity; +case ( condition 2) + :Activity; +endswitch +@enduml""", + "svg": """test?ActivityActivitycondition 1condition 2""", + "svgelement": """""", + } + with client: + response = client.post( + "/delArrow", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = r"""@startuml +start +switch (test?) +case ( condition 1) + :Activity; +endswitch +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_edit_switch_case(self, client): + test_data = { + "plantuml": r"""@startuml +start +switch (test?) +case ( condition 1) + :Activity; +case ( condition 2) + :Activity; +endswitch +@enduml""", + "svg": """test?ActivityActivitycondition 1condition 2""", + "svgelement": """""", + "text": "hello", + } + with client: + response = client.post( + "/editArrow", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = r"""@startuml +start +switch (test?) +case ( condition 1) + :Activity; +case (hello) + :Activity; +endswitch +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_edit_arrow(self, client): + test_data = { + "plantuml": r"""@startuml +start +-> Nsmf_PDUSession_CreateSMContext Request +received from AMF; +: 1. Nsmf_PDUSession_CreateSMContext Request; +-> label on arrow; +: 2. activity2; +@enduml""", + "svg": """1. Nsmf_PDUSession_CreateSMContext Request2. activity2Nsmf_PDUSession_CreateSMContext Requestreceived from AMFlabel on arrow""", + "svgelement": """""", + "text": "hello", + } + with client: + response = client.post( + "/editArrow", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = r"""@startuml +start +-> Nsmf_PDUSession_CreateSMContext Request +received from AMF; +: 1. Nsmf_PDUSession_CreateSMContext Request; +->hello; +: 2. activity2; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_getarrowtext(self, client): + test_data = { + "plantuml": r"""@startuml +start +-> Nsmf_PDUSession_CreateSMContext Request +received from AMF; +: 1. Nsmf_PDUSession_CreateSMContext Request; +-> label on arrow; +: 2. activity2; +@enduml""", + "svg": """1. Nsmf_PDUSession_CreateSMContext Request2. activity2Nsmf_PDUSession_CreateSMContext Requestreceived from AMFlabel on arrow""", + "svgelement": """""", + } + with client: + response = client.post( + "/getArrowText", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = "label on arrow" + assert response.data.decode("utf-8") == expected_result + + def test_deletearrow3(self, client): + test_data = { + "plantuml": r"""@startuml +start +-> Nsmf_PDUSession_CreateSMContext Request +received from AMF; +: 1. Nsmf_PDUSession_CreateSMContext Request; +-> label on arrow; +: 2. activity2; +@enduml""", + "svg": """1. Nsmf_PDUSession_CreateSMContext Request2. activity2Nsmf_PDUSession_CreateSMContext Requestreceived from AMFlabel on arrow""", + "svgelement": """""", + } + with client: + response = client.post( + "/delArrow", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = r"""@startuml +start +-> Nsmf_PDUSession_CreateSMContext Request +received from AMF; +: 1. Nsmf_PDUSession_CreateSMContext Request; +: 2. activity2; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_deletearrow2(self, client): + test_data = { + "plantuml": r"""@startuml +start +-> Nsmf_PDUSession_CreateSMContext Request\nreceived from AMF; +: 1. Nsmf_PDUSession_CreateSMContext Request; +-> label on arrow; +: 2. activity2; +@enduml""", + "svg": """1. Nsmf_PDUSession_CreateSMContext Request2. activity2Nsmf_PDUSession_CreateSMContext Requestreceived from AMFlabel on arrow""", + "svgelement": """""", + } + with client: + response = client.post( + "/delArrow", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = r"""@startuml +start +: 1. Nsmf_PDUSession_CreateSMContext Request; +-> label on arrow; +: 2. activity2; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_deletearrow(self, client): + test_data = { + "plantuml": r"""@startuml +start + ->Hej; + :Activity; +@enduml""", + "svg": """ActivityHej""", + "svgelement": """""", + } + with client: + response = client.post( + "/delArrow", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = r"""@startuml +start + :Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + +class TestAppRoutesTitle: + def test_edittitle_remove_title(self, client): + test_data = { + "plantuml": """@startuml +title +Placeholder Title +endtitle +:Activity 1; +@enduml""", + "title": "", + } + with client: + response = client.post( + "/editTitle", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +:Activity 1; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_addtitle_there_already_is_one(self, client): + test_data = { + "plantuml": """@startuml +title +This is a title +endtitle +:Activity 1; +@enduml""" + } + with client: + response = client.post( + "/addTitle", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +title +This is a title +endtitle +:Activity 1; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_addtitle(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +@enduml""" + } + with client: + response = client.post( + "/addTitle", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +title +Placeholder Title +endtitle +:Activity 1; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_edittitle(self, client): + test_data = { + "plantuml": """@startuml +title +Placeholder Title +endtitle +:Activity 1; +@enduml""", + "title": "Hej", + } + with client: + response = client.post( + "/editTitle", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +title +Hej +endtitle +:Activity 1; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_gettitleline(self, client): + test_data = { + "plantuml": """@startuml +title +Placeholder Title +Hello +endtitle +:Activity 1; +@enduml""" + } + with client: + response = client.post( + "/getTitleLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_result = [1, 4] + + # Assert the result value is as expected + assert result_value == expected_result + + def test_deletetitle(self, client): + test_data = { + "plantuml": """@startuml +title +Placeholder Title +Hello +endtitle +:Activity 1; +@enduml""" + } + with client: + response = client.post( + "/deleteTitle", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +:Activity 1; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_gettitle(self, client): + test_data = { + "plantuml": """@startuml +title +Placeholder Title +Hello +endtitle +:Activity 1; +@enduml""" + } + with client: + response = client.post( + "/getTextTitle", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """Placeholder Title +Hello""" + assert response.data.decode("utf-8") == expected_result + + +class TestAppRoutesConnector: + def test_get_char_connector(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +(A) +note left +note +end note +detach +@enduml""", + "svg": """Activity""", + "svgelement": """""", + } + with client: + response = client.post( + "/getCharConnector", + data=json.dumps(test_data), + content_type="application/json", + ) + expected = "A" + assert response.data.decode("utf-8") == expected + + def test_edit_colored_connector_with_colored_activity(self, client): + test_data = { + "plantuml": """@startuml +#yellow:Activity; +#green:(A) +note left +note +end note +detach +@enduml""", + "svg": """Activity""", + "svgelement": """""", + "text": "C", + } + with client: + response = client.post( + "/editCharConnector", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +#yellow:Activity; +#green:(C) +note left +note +end note +detach +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_edit_colored_connector(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +#green:(A) +note left +note +end note +detach +@enduml""", + "svg": """Activity""", + "svgelement": """""", + "text": "C", + } + with client: + response = client.post( + "/editCharConnector", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +#green:(C) +note left +note +end note +detach +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_edit_connector(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +(A) +note left +note +end note +detach +@enduml""", + "svg": """Activity""", + "svgelement": """""", + "text": "C", + } + with client: + response = client.post( + "/editCharConnector", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +(C) +note left +note +end note +detach +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_add_while_connector(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +(A) +note left +note +end note +detach +@endumll""", + "svg": """Activity""", + "svgelement": """""", + "where": "below", + "type": "while", + } + with client: + response = client.post( + "/addToConnector", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +(A) +note left +note +end note +detach +while (Statement) is (yes) +:Activity; +endwhile (no) +:Activity; +@endumll""" + assert response.data.decode("utf-8") == expected_puml + + def test_add_activity_connector2(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +(A) +note left +note +end note +detach +@endumll""", + "svg": """Activity""", + "svgelement": """""", + "where": "below", + "type": "activity", + } + with client: + response = client.post( + "/addToConnector", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +(A) +note left +note +end note +detach +:Activity; +@endumll""" + assert response.data.decode("utf-8") == expected_puml + + def test_add_notetoconnector(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +(A) +@endumll""", + "svg": """Activity""", + "svgelement": """""", + "where": "below", + "type": "note", + } + with client: + response = client.post( + "/addToConnector", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +(A) +note right +note +end note +@endumll""" + assert response.data.decode("utf-8") == expected_puml + + def test_toggle_detach2(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +(A) +note right +note +end note +@endumll""", + "svg": """Activity""", + "svgelement": """""", + } + with client: + response = client.post( + "/detachConnector", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +(A) +note right +note +end note +detach +@endumll""" + assert response.data.decode("utf-8") == expected_puml + + def test_toggle_detach(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +(A) +detach +@endumll""", + "svg": """Activity""", + "svgelement": """""", + } + with client: + response = client.post( + "/detachConnector", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +(A) +@endumll""" + assert response.data.decode("utf-8") == expected_puml + + def test_deleteconnectorwithnote(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +(A) +note left +note +end note +@endumll""", + "svg": """Activity""", + "svgelement": """""", + } + with client: + response = client.post( + "/connectorDelete", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +@endumll""" + assert response.data.decode("utf-8") == expected_puml + + def test_deleteconnector(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +(A) +detach +@endumll""", + "svg": """Activity""", + "svgelement": """""", + } + with client: + response = client.post( + "/connectorDelete", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +@endumll""" + assert response.data.decode("utf-8") == expected_puml + + def test_get_connector_line(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +(A) +detach +@endumll""", + "svg": """Activity""", + "svgelement": """""", + } + with client: + response = client.post( + "/getConnectorLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_puml = 3 + + # Assert the result value is as expected + assert result_value == expected_puml + + +class TestAppRoutesEllipse: + def test_addconnectorbelowwithconnector(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +(A) +detach +stop +@enduml""", + "svg": """Activity""", + "svgelement": """""", + "where": "below", + "type": "connector", + } + with client: + response = client.post( + "/addToEllipse", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +:Activity; +(A) +detach +stop +(C) +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addactivitybelowwithconnector(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +(A) +detach +stop +@enduml""", + "svg": """Activity""", + "svgelement": """""", + "where": "below", + "type": "activity", + } + with client: + response = client.post( + "/addToEllipse", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +:Activity; +(A) +detach +stop +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_deleteellipse(self, client): + test_data = { + "plantuml": """@startuml +start +stop +@enduml""", + "svg": """""", + "svgelement": """""", + } + with client: + response = client.post( + "/deleteEllipse", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_get_ellipse_line(self, client): + test_data = { + "plantuml": """@startuml +start +stop +@enduml""", + "svg": """""", + "svgelement": """""", + } + with client: + response = client.post( + "/getEllipseLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_puml = 3 + + # Assert the result value is as expected + assert result_value == expected_puml + + +class TestAppRoutesNote: + def test_togglenoteleft(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +note left +Hello +end note +@enduml""", + "svg": """HelloActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/noteToggle", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +:Activity; +note right +Hello +end note +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_togglenote(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +note right +Hello +end note +@enduml""", + "svg": """HelloActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/noteToggle", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +:Activity; +note left +Hello +end note +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_deletenote(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +note right +Hello +end note +@enduml""", + "svg": """HelloActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/deleteNote", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_get_note_line(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +note right +Hello +end note +@enduml""", + "svg": """HelloActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/getNoteLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_puml = [3, 5] + + # Assert the result value is as expected + assert result_value == expected_puml + + def test_editnote_empty(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +note right +Hello +end note +@enduml""", + "svg": """HelloActivity""", + "svgelement": """""", + "text": "", + } + with client: + response = client.post( + "/editNote", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def test_editnote(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +note right +Hello +end note +@enduml""", + "svg": """HelloActivity""", + "svgelement": """""", + "text": """Bom""", + } + with client: + response = client.post( + "/editNote", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = """@startuml +start +:Activity; +note right +Bom +end note +@enduml""" + assert response.data.decode("utf-8") == expected_result + + def testgettextnote(self, client): + test_data = { + "plantuml": """@startuml +start +:Activity; +note right +Hello +end note +@enduml""", + "svg": """HelloActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/getNoteText", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = "Hello" + assert response.data.decode("utf-8") == expected_result + + def testgettextnotebug(self, client): + test_data = { + "plantuml": """@startuml +title +This is the title of this diagram +endtitle +start +:Link to [[google.com Start]]; +note right +start +end note +if (Statement) then (yes) + :Activity; +while (While ?) is (yes) + :Activity; +endwhile (no) +#red:Activity; +note left +end +end note +else (no) + :Activity; +group group +if (Statement) then (yes) + :Activity; +(C) +detach +else (no) +#lightgreen:Activity; +note right +hej +end note +fork + :action; +stop +fork again + :action; +fork again + :action; +note right +action +end note +end merge +endif +end group +partition partition { +while (While ?) is (yes) + :Activity; +endwhile (no) +(C) +note right +end +end note +} +endif +detach +@enduml +""", + "svg": """This is the title of this diagramstartLink toStartStatementyesnoActivityActivityyesWhile ?noendActivityActivitygroupStatementyesnoActivityhejActivityactionactionactionactionpartitionActivityyesWhile ?noend""", + "svgelement": """""", + } + with client: + response = client.post( + "/getNoteText", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = "hej" + assert response.data.decode("utf-8") == expected_result + + +class TestAppRoutesSwitch: + def test_gettext_switch(self, client): + test_data = { + "plantuml": """@startuml +switch (asdfasdfasdf) +case ( condition A ) +:Text 1; +switch (hej) +case ( condition A ) +:Text 1; +case ( condition B ) +:Text 2; +case ( condition C ) +:Text 3; +endswitch +case ( condition B ) +:Text 2; +case ( condition C ) +:Text 3; +endswitch +@enduml""", + "svg": """asdfasdfasdfText 1hejText 1Text 2Text 3Text 2Text 3condition Acondition Ccondition Bcondition Acondition Ccondition B""", + "svgelement": """""", + } + with client: + response = client.post( + "/getTextPoly", + data=json.dumps(test_data), + content_type="application/json", + ) + output = ["asdfasdfasdf", "", ""] + json.loads(response.data.decode("utf-8")) == output + + def test_switch_again2(self, client): + test_data = { + "plantuml": """@startuml +switch (test?) +case ( condition A) +:Text 1; +case ( condition B) +:Text 2; +case ( condition 2) +:Text 3; +endswitch +@enduml""", + "svg": """test?Text 1Text 2Text 3condition Acondition Ccondition B""", + "svgelement": """""", + } + with client: + response = client.post( + "/switchAgain", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +switch (test?) +case ( condition A) +:Text 1; +case ( condition B) +:Text 2; +case ( condition 2) +:Text 3; +case ( condition 3) +:Activity; +endswitch +@enduml""" + + assert response.data.decode("utf-8") == expected_puml + + def test_switch_again(self, client): + test_data = { + "plantuml": """@startuml +switch (test?) +case ( condition A ) +:Text 1; +case ( condition B ) +:Text 2; +case ( condition C ) +:Text 3; +endswitch +@enduml""", + "svg": """test?Text 1Text 2Text 3condition Acondition Ccondition B""", + "svgelement": """""", + } + with client: + response = client.post( + "/switchAgain", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +switch (test?) +case ( condition A ) +:Text 1; +case ( condition B ) +:Text 2; +case ( condition C ) +:Text 3; +case ( condition 1) +:Activity; +endswitch +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_edit_switch1(self, client): + test_data = { + "plantuml": """@startuml +switch (test?) +case ( condition A ) +:Text 1; +case ( condition B ) +:Text 2; +case ( condition C ) +:Text 3; +endswitch +@enduml""", + "svg": """test?Text 1Text 2Text 3condition Acondition Ccondition B""", + "statement": "State", + "branch1": "bam", + "branch2": "bom", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +switch (State) +case ( condition A ) +:Text 1; +case ( condition B ) +:Text 2; +case ( condition C ) +:Text 3; +endswitch +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + +class TestAppRoutesRepeatWhile: + def test_checkrepeathasbackward2(self, client): + test_data = { + "plantuml": """@startuml +repeat + :Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """Activitynowhile ?yesActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/checkIfRepeatHasBackward", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """empty""" + assert response.data.decode("utf-8") == expected_puml + + def test_checkrepeathasbackward(self, client): + test_data = { + "plantuml": """@startuml +repeat + :Activity; +backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """Activitynowhile ?yesActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/checkIfRepeatHasBackward", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """backward""" + assert response.data.decode("utf-8") == expected_puml + + def test_checkrepeat(self, client): + test_data = { + "plantuml": """@startuml +repeat + :Activity; +backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """Activitynowhile ?yesActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/checkWhatPoly", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """repeat""" + assert response.data.decode("utf-8") == expected_puml + + def test_gettext_repeatwhile(self, client): + test_data = { + "plantuml": """@startuml +repeat + :Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """Activitynowhile ?yesActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/getTextPoly", + data=json.dumps(test_data), + content_type="application/json", + ) + output = ["while ?", "no", "yes"] + json.loads(response.data.decode("utf-8")) == output + + def test_add_backwards_already_exists(self, client): + test_data = { + "plantuml": """@startuml +repeat + :Activity; +backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """Activitynowhile ?yesActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/addBackwards", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +repeat + :Activity; +backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_add_backwards(self, client): + test_data = { + "plantuml": """@startuml +repeat + :Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """Activitynowhile ?yesActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/addBackwards", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +repeat + :Activity; +backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_add_repeat_while(self, client): + test_data = { + "plantuml": """@startuml +start +repeat + :b; + :b; + repeat + :aa; + :aa; +repeat while (a) is (a) not (a) +repeat while (b) is (b) not (b) +@enduml""", + "svg": """bbaaaaaaabbb""", + "svgelement": """""", + "where": "right", + "type": "fork", + } + with client: + response = client.post( + "/addToIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +repeat + :b; + :b; + repeat + :aa; + :aa; +repeat while (a) is (a) not (a) +repeat while (b) is (b) not (b) +fork +:action; +fork again +:action; +end fork +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_edit_repeatwhile4(self, client): + test_data = { + "plantuml": """@startuml +start +repeat +:activity; +repeat while (hej) is (yes) not (no) +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""", + "svg": """activitynohejyesStatementyesnoActivityActivity""", + "statement": "State", + "branch1": "bam", + "branch2": "bom", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +repeat +:activity; +repeat while (hej) is (yes) not (no) +if (State) then (bam) + :Activity; +else (bom) + :Activity; +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_edit_repeatwhile3(self, client): + test_data = { + "plantuml": """@startuml +start +repeat +:activity; +repeat while (hej) is (yes) not (no) +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""", + "svg": """activitynohejyesStatementyesnoActivityActivity""", + "statement": "State", + "branch1": "bam", + "branch2": "bom", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +repeat +:activity; +repeat while (State) is (bom) not (bam) +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_edit_repeatwhile2(self, client): + test_data = { + "plantuml": """@startuml +start +repeat + :b; + :b; + repeat + :aa; + :aa; +repeat while (a) is (a) not (a) +repeat while (b) is (b) not (b) +@enduml""", + "svg": """bbaaaaaaabbb""", + "statement": "State", + "branch1": "bam", + "branch2": "bom", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +repeat + :b; + :b; + repeat + :aa; + :aa; +repeat while (a) is (a) not (a) +repeat while (State) is (bom) not (bam) +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_nested_repeatwhile(self, client): + test_data = { + "plantuml": """@startuml +start +repeat + :b; + :b; + repeat + :aa; + :aa; +repeat while (a) is (a) not (a) +repeat while (b) is (b) not (b) +@enduml""", + "svg": """bbaaaaaaabbb""", + "svgelement": """""", + } + with client: + response = client.post( + "/delIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +repeat + :b; + :b; +repeat while (b) is (b) not (b) +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_deleteifnestedinrepeatwhile(self, client): + test_data = { + "plantuml": """@startuml +start +repeat +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif + :read data; + :generate diagrams; +repeat while (more data?) is (yes) not (no) +if (Bom) then (y) + :Activity; +else (n) + :Activity; +endif +@enduml""", + "svg": """StatementyesnoActivityActivityread datagenerate diagramsnomore data?yesBomynActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/delIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +repeat + :read data; + :generate diagrams; +repeat while (more data?) is (yes) not (no) +if (Bom) then (y) + :Activity; +else (n) + :Activity; +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_deleteifwithrepeatwhileabove(self, client): + test_data = { + "plantuml": """@startuml +start +repeat + :read data; + :generate diagrams; +repeat while (more data?) is (yes) not (no) +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""", + "svg": """read datagenerate diagramsnomore data?yesStatementyesnoActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/delIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +repeat + :read data; + :generate diagrams; +repeat while (more data?) is (yes) not (no) +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + +class TestAppRoutesIf: + def test_build_tree(self): + lines = """@startuml +:Activity 1; +if (st0) then (yes) + if (st1) then (yes) + :activity 2; + else (no) + endif + if (st2) then (yes) + if (st3) then (yes) + else (no) + :activity 3; + endif + else (no) + endif + :activity 4; +endif +@enduml""".splitlines() + output = [3, 8, 7, 2] + assert build_tree(lines) == output + + def test_addforkrightbranchinsiderepeat(self, client): + test_data = { + "plantuml": """@startuml +start +repeat +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +repeat while (hej) is (yes) not (no) +:activity; +@enduml""", + "svg": """StatementyesnoActivityActivitynohejyesactivity""", + "svgelement": """""", + "where": "right", + "type": "fork", + } + with client: + response = client.post( + "/addToIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +repeat +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +fork +:action; +fork again +:action; +end fork +endif +repeat while (hej) is (yes) not (no) +:activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addforkrightbranch(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""", + "svg": """Activity 1StatementyesnoActivityActivity""", + "svgelement": """""", + "where": "right", + "type": "fork", + } + with client: + response = client.post( + "/addToIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +fork +:action; +fork again +:action; +end fork +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addrepeatleftbranch(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""", + "svg": """Activity 1StatementyesnoActivityActivity""", + "svgelement": """""", + "where": "left", + "type": "repeat", + } + with client: + response = client.post( + "/addToIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +repeat +:Activity; +backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +else (no) + :Activity; +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_toggledetach2(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +detach +@enduml""", + "svg": """Activity 1StatementyesnoActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/detachIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_toggledetach(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""", + "svg": """Activity 1StatementyesnoActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/detachIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +detach +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_editifwithwhileabove(self, client): + test_data = { + "plantuml": """@startuml +start +while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +:hello again; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +stop +@enduml""", + "svg": """helloYesST_ONGOINGNohello againStatementyesnoActivityActivity""", + "statement": "State", + "branch1": "bam", + "branch2": "bom", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +while (ST_ONGOING) is (Yes) + :hello; +endwhile (No); +:hello again; +if (State) then (bam) + :Activity; +else (bom) + :Activity; +endif +stop +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_gettextpolynobranch(self, client): + test_data = { + "plantuml": """@startuml +if (Statement) then (yes) + :Activity; + :Activity; +endif +@enduml""", + "svg": """ActivityActivityyesStatement""", + "svgelement": """""", + } + with client: + response = client.post( + "/getTextPoly", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = ["Statement", "yes"] + assert json.loads(response.data.decode("utf-8")) == expected_result + + def test_editnoelsebranchempty(self, client): + test_data = { + "plantuml": """@startuml +if (Statement) then (yes) + :Activity; + :Activity; +endif +@enduml""", + "svg": """ActivityActivityyesStatement""", + "statement": "Statement", + "branch1": "yes", + "branch2": "", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +if (Statement) then (yes) + :Activity; + :Activity; +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_editnoelsebranch(self, client): + test_data = { + "plantuml": """@startuml +if (Statement) then (yes) + :Activity; + :Activity; +endif +@enduml""", + "svg": """ActivityActivityyesStatement""", + "statement": "Statement", + "branch1": "yes", + "branch2": "right", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +if (Statement) then (yes) + :Activity; + :Activity; +else (right) +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addleftifnested(self, client): + test_data = { + "plantuml": """@startuml +:activity; +if (Statement) then (yes) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +else (no) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +endif +@enduml""", + "svg": """activityStatementyesnoActivityStatementyesnoActivityActivityActivityStatementyesnoActivityActivity""", + "svgelement": """""", + "where": "left", + "type": "if", + } + with client: + response = client.post( + "/addToIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:activity; +if (Statement) then (yes) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +if (Statement) then (yes) +:Activity; +else (no) +:Activity; +endif +else (no) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_get_if_line(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +detach +else (no) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +endif +@enduml""", + "svg": """Activity 1StatementyesnoActivityStatementyesnoActivityActivityActivityStatementyesnoActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/getIfLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_puml = [4, 6, 8] + + # Assert the result value is as expected + assert result_value == expected_puml + + def test_get_if_line2(self, client): + test_data = { + "plantuml": """@startuml +start +repeat + :Activity; + backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """Activitynowhile ?yesActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/getIfLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_puml = 5 + + # Assert the result value is as expected + assert result_value == expected_puml + + def test_get_if_line3(self, client): + test_data = { + "plantuml": """@startuml +start +switch (test?) +case ( condition 1) + :Activity; +case ( condition 2) + :Activity; +endswitch +@enduml""", + "svg": """test?ActivityActivitycondition 1condition 2""", + "svgelement": """""", + } + with client: + response = client.post( + "/getIfLine", + data=json.dumps(test_data), + content_type="application/json", + ) + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + # Expected value + expected_puml = [2, 7] + + # Assert the result value is as expected + assert result_value == expected_puml + + def test_deleterepeatweirdnest(self, client): + test_data = { + "plantuml": """@startuml + +start +if ( 8. IUPF is selected or not in step 4) then (yes) + :PFCP Session Establishment Request with IUPF; + repeat + :Start T1 timer; + if (Wait) then (PFCP Session Establishment Response) + break + endif + -> T1 Timeout; + backward:9. Retransmit PFCP Session Establishment Request with IUPF; + repeat while (Number of retries < N1?) is (Yes) not (No) + if (PFCP Session Establishment Positive Response?) then (No) + :5.- Nsmf_PDUSession_CreateSMContext Negative Response + - Release session without sending N1N2 to AMF; + stop + endif + -> Yes; +endif +:10. Nsmf_PDUSession_CreateSMContext Response with hoState == PREPARING; +: Start Tsrn2 Timer; +repeat + if (Wait) then (Tsrn2 timeout) + :11. Start t4to5ho timer; + if (Wait) then (t4to5ho timeout) + :12. Release Session; + stop + else (Nsmf_PDUSession_UpdateSMContext Request \n hoState == CANCELLED) + :13. Rollback to EPS; + stop + endif + else (Nsmf_PDUSession_UpdateSMContext Request) + endif + backward: 14. Nsmf_PDUSession_UpdateSMContext Negative Response; +repeat while (Message format is valid?) is (No) not (Yes) +:Activity; +@enduml""", + "svg": """PFCP Session Establishment Request with IUPFStart T1 timerPFCP Session Establishment ResponseWaitNoNumber of retries < N1?Yes9. Retransmit PFCP Session Establishment Request with IUPF5.- Nsmf_PDUSession_CreateSMContext Negative Response- Release session without sending N1N2 to AMFNoPFCP Session Establishment Positive Response?yes8. IUPF is selected or not in step 4Yes10. Nsmf_PDUSession_CreateSMContext Response with hoState == PREPARINGStart Tsrn2 Timer11. Start t4to5ho timerWaitt4to5ho timeoutNsmf_PDUSession_UpdateSMContext RequesthoState == CANCELLED12. Release Session13. Rollback to EPSTsrn2 timeoutWaitNsmf_PDUSession_UpdateSMContext RequestYesMessage format is valid?No14. Nsmf_PDUSession_UpdateSMContext Negative ResponseActivityT1 Timeout""", + "svgelement": """""", + } + with client: + response = client.post( + "/delIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml + +start +if ( 8. IUPF is selected or not in step 4) then (yes) + :PFCP Session Establishment Request with IUPF; + repeat + :Start T1 timer; + if (Wait) then (PFCP Session Establishment Response) + break + endif + -> T1 Timeout; + backward:9. Retransmit PFCP Session Establishment Request with IUPF; + repeat while (Number of retries < N1?) is (Yes) not (No) + if (PFCP Session Establishment Positive Response?) then (No) + :5.- Nsmf_PDUSession_CreateSMContext Negative Response + - Release session without sending N1N2 to AMF; + stop + endif + -> Yes; +endif +:10. Nsmf_PDUSession_CreateSMContext Response with hoState == PREPARING; +: Start Tsrn2 Timer; +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_deleteifstatementnested2(self, client): + test_data = { + "plantuml": """@startuml +if (hoState) then (hoState == PREPARED) + if (Check N2 IE) then (N2 Handover Request Acknowledge Transfer) + if (Default QoS flow in QoSFlowSetupResponseList IE?) then (Yes) + else (No) + :15. Nsmf_PDUSession_UpdateSMContext Negative Response; + :Start t4to5ho timer; + if (Wait) then (t4to5ho Timeout) + :16 Release Session; + stop + else (Nsmf_PDUSession_UpdateSMContext Request \n hoState == CANCELLED) + :13. Rollback to EPS; + stop + endif + endif + :17. Handle optional AMF Change; + :18. Nsmf_PDUSession_UpdateSMContext Response with hoState == PREPARED; + else (N2 Handover Resource Allocation Unsuccessful Transfer) + :19. Nsmf_PDUSession_UpdateSMContext Negative Response; + : Start t4to5ho timer; + if (Wait) then (t4to5ho Timeout) + :20. Release Session; + stop + else (Nsmf_PDUSession_UpdateSMContext Request \n hoState == CANCELLED) + :13. Rollback to EPS; + stop + endif + endif +else (hoState == CANCELLED) + : 13. Rollback to EPS; + stop +endif +end +@enduml""", + "svg": """hoStatehoState == PREPAREDhoState == CANCELLEDCheck N2 IEN2 Handover Request Acknowledge TransferN2 Handover Resource Allocation Unsuccessful Transfer15. Nsmf_PDUSession_UpdateSMContext Negative ResponseStart t4to5ho timerWaitt4to5ho TimeoutNsmf_PDUSession_UpdateSMContext RequesthoState == CANCELLED16 Release Session13. Rollback to EPSNoDefault QoS flow in QoSFlowSetupResponseList IE?Yes17. Handle optional AMF Change18. Nsmf_PDUSession_UpdateSMContext Response with hoState == PREPARED19. Nsmf_PDUSession_UpdateSMContext Negative ResponseStart t4to5ho timerWaitt4to5ho TimeoutNsmf_PDUSession_UpdateSMContext RequesthoState == CANCELLED20. Release Session13. Rollback to EPS13. Rollback to EPS""", + "svgelement": """""", + } + with client: + response = client.post( + "/delIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +if (hoState) then (hoState == PREPARED) + if (Check N2 IE) then (N2 Handover Request Acknowledge Transfer) + if (Default QoS flow in QoSFlowSetupResponseList IE?) then (Yes) + else (No) + :15. Nsmf_PDUSession_UpdateSMContext Negative Response; + :Start t4to5ho timer; + endif + :17. Handle optional AMF Change; + :18. Nsmf_PDUSession_UpdateSMContext Response with hoState == PREPARED; + else (N2 Handover Resource Allocation Unsuccessful Transfer) + :19. Nsmf_PDUSession_UpdateSMContext Negative Response; + : Start t4to5ho timer; + if (Wait) then (t4to5ho Timeout) + :20. Release Session; + stop + else (Nsmf_PDUSession_UpdateSMContext Request \n hoState == CANCELLED) + :13. Rollback to EPS; + stop + endif + endif +else (hoState == CANCELLED) + : 13. Rollback to EPS; + stop +endif +end +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_deleteifstatementnestedemptyelse(self, client): + test_data = { + "plantuml": """@startuml +start +if (Statement) then (yes) + :Activity; + if (Statement) then (yes) + :Activity; + else (no) + :Activity; + endif + :Activity; +else (no) +endif +@enduml""", + "svg": """ActivityStatementyesnoActivityActivityActivityyesStatement""", + "svgelement": """""", + } + with client: + response = client.post( + "/delIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_deleteifstatementnestednoelse(self, client): + test_data = { + "plantuml": """@startuml +start +if (Statement) then (yes) + :Activity; + if (Statement) then (yes) + :Activity; + else (no) + :Activity; + endif + :Activity; +endif +@enduml""", + "svg": """ActivityStatementyesnoActivityActivityActivityyesStatement""", + "svgelement": """""", + } + with client: + response = client.post( + "/delIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_deleteifstatement_check_branch_cov(self, client): + test_data = { + "plantuml": """@startuml +start +if (Statement) then (yes) + :Activity; +else (no) + stop +endif +if (Statement) then (yes) + stop +else (no) + (A) +endif +end +@enduml""", + "svg": """StatementyesnoStatementyesno""", + "svgelement": """""", + } + with client: + response = client.post( + "/delIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +if (Statement) then (yes) + :Activity; +else (no) + stop +endif +end +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_deleteifstatement(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +detach +else (no) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +endif +@enduml""", + "svg": """Activity 1StatementyesnoActivityStatementyesnoActivityActivityActivityStatementyesnoActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/delIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_get_text_no_branchif(self, client): + test_data = { + "plantuml": """@startuml + +start +-> 1; +: 1. Nsmf_PDUSession_CreateSMContext Request; +if (statement1a) then (downa) + :2; + stop +else (righta) +endif + +:3; +:4; +if (statement2) then (down) + :5; + stop +else (right) +endif +:Activity; +@enduml""", + "svg": """1. Nsmf_PDUSession_CreateSMContext Request2downastatement1arighta345downstatement2rightActivity1""", + "svgelement": """""", + } + with client: + response = client.post( + "/getTextPoly", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = ["statement2", "down", "right"] + assert json.loads(response.data.decode("utf-8")) == expected_result + + def test_gettextpoly(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +if (Statement) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""", + "svg": """Activity 1StatementyesnoActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/getTextPoly", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_result = ["Statement", "yes", "no"] + assert json.loads(response.data.decode("utf-8")) == expected_result + + def test_editmultilineifstatement(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +if (hej +hej) then (yes +yes) + :Activity; +else (no +no) + :Activity; +endif +@enduml""", + "svg": """Activity 1hejhejyesyesnonoActivityActivity""", + "statement": "hej", + "branch1": "yes", + "branch2": "no", + "svgelement": """""", + } + with client: + response = client.post( + "/editTextIf", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity 1; +if (hej) then (yes) + :Activity; +else (no) + :Activity; +endif +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + +class TestAppRoutesFork: + def test_delfork3(self, client): + test_data = { + "plantuml": """@startuml +start +fork + :action; +fork again + :action; +fork + :action; +fork again + :action; +end fork +end fork + :Activity; +@enduml""", + "line": 11, + } + with client: + response = client.post( + "/deleteFork2", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start + :Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_delfork2(self, client): + test_data = { + "plantuml": """@startuml +start +fork + :action; +fork again + :action; +end fork +@enduml""", + "line": 6, + } + with client: + response = client.post( + "/deleteFork2", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_togglefork3(self, client): + test_data = { + "plantuml": """@startuml +fork + :action; +end fork +@enduml""", + "line": 3, + } + with client: + response = client.post( + "/forkToggle2", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +fork + :action; +end merge +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_togglefork2(self, client): + test_data = { + "plantuml": """@startuml +start +fork + :action; +fork again + :action; +end fork +@enduml""", + "line": 6, + } + with client: + response = client.post( + "/forkToggle2", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +fork + :action; +fork again + :action; +end merge +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addtofork(self, client): + test_data = { + "plantuml": """@startuml +start +fork + :action; +fork again + :action; +end fork +@enduml""", + "line": 6, + "type": "activity", + } + with client: + response = client.post( + "/addToFork", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +fork + :action; +fork again + :action; +end fork +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_forktogglefork(self, client): + test_data = { + "plantuml": """@startuml +start +fork + :action 1; +fork again + :action 2; +fork again + :action 2; +end merge +stop +@enduml""", + "svg": """action 1action 2action 2""", + "svgelement": """""", + } + with client: + response = client.post( + "/forkToggle", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +fork + :action 1; +fork again + :action 2; +fork again + :action 2; +end fork +stop +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_forktogglemerge(self, client): + test_data = { + "plantuml": """@startuml +start +fork + :action 1; +fork again + :action 2; +fork again + :action 2; +end fork +stop +@enduml""", + "svg": """action 1action 2action 2""", + "svgelement": """""", + } + with client: + response = client.post( + "/forkToggle", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +fork + :action 1; +fork again + :action 2; +fork again + :action 2; +end merge +stop +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_forkagainwithnestedmerge(self, client): + test_data = { + "plantuml": """@startuml +fork + :action; +fork again + :action; +fork + :action; +fork again + :action; +fork again + :action; +end merge +fork again + :action; +fork + :action; +fork again + :action; +fork + :action; +fork again + :acation; +end fork +fork again + :actioasdn; +fork again + :action; +fork again + :action; +end fork +end fork +stop +@enduml""", + "svg": """actionactionactionactionactionactionactionactionactionacationactioasdnactionaction""", + "svgelement": """""", + } + with client: + response = client.post( + "/forkAgain", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +fork + :action; +fork again + :action; +fork + :action; +fork again + :action; +fork again + :action; +end merge +fork again + :action; +fork + :action; +fork again + :action; +fork + :action; +fork again + :acation; +end fork +fork again + :actioasdn; +fork again + :action; +fork again + :action; +fork again + :action; +end fork +end fork +stop +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_forkagain(self, client): + test_data = { + "plantuml": """@startuml +start +fork + :action 1; +fork again + :action 2; +fork again + :action 2; +end fork +stop +@enduml""", + "svg": """action 1action 2action 2""", + "svgelement": """""", + } + with client: + response = client.post( + "/forkAgain", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +fork + :action 1; +fork again + :action 2; +fork again + :action 2; +fork again + :action; +end fork +stop +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_deletenestedfork(self, client): + test_data = { + "plantuml": """@startuml +start +fork + :action 2; +fork +:action 1; +fork again +:action 2; +end fork +fork again + :action 2; +fork +:action 1; +fork again +:action 2; +end fork +end fork +stop +@enduml""", + "svg": """action 2action 1action 2action 2action 1action 2""", + "svgelement": """""", + } + with client: + response = client.post( + "/deleteFork", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +fork + :action 2; +fork +:action 1; +fork again +:action 2; +end fork +fork again + :action 2; +end fork +stop +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_deletefork(self, client): + test_data = { + "plantuml": """@startuml +start +fork + :action 1; +fork again + :action 2; +fork again + :action 2; +end fork +stop +@enduml""", + "svg": """action 1action 2action 2""", + "svgelement": """""", + } + with client: + response = client.post( + "/deleteFork", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +stop +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + +class TestAppRoutesActivity: + def test_break_colored_activity_with_colored_connector(self, client): + test_data = { + "plantuml": """@startuml +start +#green:(A) +#red:Activity; +:Activity; +@enduml""", + "svg": """ActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/breakActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +#green:(A) +#red:Activity; +break +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addarrow3(self, client): + test_data = { + "plantuml": """@startuml +start +-> Arrow label 1; +:Activity; +:Activity; +@enduml""", + "svg": """ActivityActivityArrow label 1""", + "svgelement": """""", + "where": "below", + } + with client: + response = client.post( + "/addArrowLabel", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +-> Arrow label 1; +:Activity; +:Activity; +-> Arrow label 2; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addarrow2(self, client): + test_data = { + "plantuml": """@startuml +start + :Activity; +@enduml""", + "svg": """Activity""", + "svgelement": """""", + "where": "below", + } + with client: + response = client.post( + "/addArrowLabel", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start + :Activity; +-> Arrow label 1; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addarrow(self, client): + test_data = { + "plantuml": """@startuml +start + :Activity; +@enduml""", + "svg": """Activity""", + "svgelement": """""", + "where": "above", + } + with client: + response = client.post( + "/addArrowLabel", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +-> Arrow label 1; + :Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_break3(self, client): + test_data = { + "plantuml": """@startuml + :Activity; +break +@enduml""", + "svg": """Activity""", + "svgelement": """""", + } + with client: + response = client.post( + "/breakActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml + :Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_detach2(self, client): + test_data = { + "plantuml": """@startuml + :Activity; +break +@enduml""", + "svg": """Activity""", + "svgelement": """""", + } + with client: + response = client.post( + "/detachActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml + :Activity; +detach +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_break2(self, client): + test_data = { + "plantuml": """@startuml + :Activity; +detach +@enduml""", + "svg": """Activity""", + "svgelement": """""", + } + with client: + response = client.post( + "/breakActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml + :Activity; +break +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_break(self, client): + test_data = { + "plantuml": """@startuml + :Activity; +note right +note +end note +@enduml""", + "svg": """Activity""", + "svgelement": """""", + } + with client: + response = client.post( + "/breakActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml + :Activity; +note right +note +end note +break +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_checkbackward(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +repeat + :Activity; +backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""", + "svg": """ActivityActivitynowhile ?yesActivityActivity""", + "svgelement": """""", + } + with client: + response = client.post( + "/checkBackward", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """backward:Activity;""" + assert response.data.decode("utf-8") == expected_puml + + def test_activity_indexes(self): + output = [3, 5, 7, 6, 10, 9, 12] + lines = """@startuml +start +repeat +:Activity; +repeat +#blue:Activity; +backward:Activity2; +:Activity; +repeat while (while ?) is (yes) not (no) +backward:Activity2; +:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""".splitlines() + assert activity_indices(lines, 0) == output + + def test_editcoloredactivity(self, client): + test_data = { + "plantuml": """@startuml +#lightblue:PPSI = 1, PPEI = 0; +@enduml""", + "svg": """PPSI = 1, PPEI = 0""", + "newname": "Hej", + "oldname": "PPSI = 1, PPEI = 0", + "svgelement": """""", + } + with client: + response = client.post( + "/editText", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +#lightblue:Hej; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_toggledetach2(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +note right +note +end note +detach +@enduml""", + "svg": """activity 1""", + "svgelement": """""", + } + with client: + response = client.post( + "/detachActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +note right +note +end note +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addnote2(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +@enduml""", + "svg": """activity 1""", + "svgelement": """""", + } + with client: + response = client.post( + "/addNoteActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +note right +note +end note +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addswitchtoactivity(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +@enduml""", + "svg": """activity 1""", + "svgelement": """""", + "type": "switch", + } + with client: + response = client.post( + "/addToActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +switch (test?) +case ( condition 1) +:Activity; +case ( condition 2) +:Activity; +endswitch +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addtoactivity(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +@enduml""", + "svg": """activity 1""", + "svgelement": """""", + "type": "repeat", + } + with client: + response = client.post( + "/addToActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +repeat +:Activity; +backward:Activity; +repeat while (while ?) is (yes) not (no) +:Activity; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_addnote(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +note right +note +end note +@enduml""", + "svg": """activity 1""", + "svgelement": """""", + } + with client: + response = client.post( + "/addNoteActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +note right +note +end note +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_toggledetach(self, client): + test_data = { + "plantuml": """@startuml +:Activity; +note right +note +end note +@enduml""", + "svg": """activity 1""", + "svgelement": """""", + } + with client: + response = client.post( + "/detachActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity; +note right +note +end note +detach +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_edit_text_empty(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +:Activity 2; +:Activity 3; +:Activity 4; +@enduml""", + "svg": """Activity 1Activity 2Activity 3Activity 4""", + "newname": "", + "oldname": "Activity 3", + "svgelement": """""", + } + with client: + response = client.post( + "/editText", data=json.dumps(test_data), content_type="application/json" + ) + expected_puml = """@startuml +:Activity 1; +:Activity 2; +:Activity 4; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_edit_text_multiline(self, client): + test_data = { + "plantuml": """@startuml + :Activity + ASdasdasd + asdasdasd; +@enduml""", + "svg": """ActivityASdasdasdasdasdasd""", + "newname": "Hej", + "oldname": "Activity 3", + "svgelement": """""", + } + with client: + response = client.post( + "/editText", data=json.dumps(test_data), content_type="application/json" + ) + expected_puml = """@startuml + :Hej; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_edit_text(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +:Activity 2; +:Activity 3; +:Activity 4; +@enduml""", + "svg": """Activity 1Activity 2Activity 3Activity 4""", + "newname": "Hej", + "oldname": "Activity 3", + "svgelement": """""", + } + with client: + response = client.post( + "/editText", data=json.dumps(test_data), content_type="application/json" + ) + expected_puml = """@startuml +:Activity 1; +:Activity 2; +:Hej; +:Activity 4; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_get_text_link(self, client): + test_data = { + "plantuml": """@startuml +start +:Link to [[google.com Google]]; +stop +@enduml""", + "svg": """Link toGoogle""", + "svgelement": """""", + } + with client: + response = client.post( + "/getText", data=json.dumps(test_data), content_type="application/json" + ) + expected_output = """Link to [[google.com Google]]""" + assert response.data.decode("utf-8") == expected_output + + def test_get_text(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +:Activity 2; +:Activity 3 +Hej +Bom; +:Activity 4; +@enduml""", + "svg": """Activity 1Activity 2Activity 3HejBomActivity 4""", + "svgelement": """""", + } + with client: + response = client.post( + "/getText", data=json.dumps(test_data), content_type="application/json" + ) + expected_output = """Activity 3 +Hej +Bom""" + assert response.data.decode("utf-8") == expected_output + + def test_delete_activitywithnote(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +:Activity 2; +note right +note +end note +detach +:Activity 3; +:Activity 4; +@enduml""", + "svg": """Activity 1noteActivity 2Activity 3Activity 4""", + "svgelement": """""", + } + with client: + response = client.post( + "/deleteActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity 1; +:Activity 3; +:Activity 4; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_delete_activity2(self, client): + test_data = { + "plantuml": """@startuml +start +repeat +:Activity; +repeat +:Activity; +backward:Activity2; +:Activity; +repeat while (while ?) is (yes) not (no) +backward:Activity2; +:Activity; +repeat while (while ?) is (yes) not (no) +@enduml""", + "svg": """ActivityActivityActivitynowhile ?yesActivity2Activitynowhile ?yesActivity2""", + "svgelement": """""", + } + with client: + response = client.post( + "/deleteActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +start +repeat +:Activity; +repeat +:Activity; +:Activity; +repeat while (while ?) is (yes) not (no) +backward:Activity2; +:Activity; +repeat while (while ?) is (yes) not (no) +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_delete_activity(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +:Activity 2; +:Activity 3; +:Activity 4; +@enduml""", + "svg": """Activity 1Activity 2Activity 3Activity 4""", + "svgelement": """""", + } + with client: + response = client.post( + "/deleteActivity", + data=json.dumps(test_data), + content_type="application/json", + ) + expected_puml = """@startuml +:Activity 1; +:Activity 2; +:Activity 4; +@enduml""" + assert response.data.decode("utf-8") == expected_puml + + def test_activity_line(self, client): + test_data = { + "plantuml": """@startuml +:Activity 1; +:Activity 2; +:Activity 3; +:Activity 4; +@enduml""", + "svg": """Activity 1Activity 2Activity 3Activity 4""", + "svgelement": """""", + } + with client: + response = client.post( + "/getActivityLine", + data=json.dumps(test_data), + content_type="application/json", + ) + + response_json = json.loads(response.data.decode("utf-8")) + result_value = response_json.get("result") + + expected_output = [3, 3] + assert result_value == expected_output diff --git a/tests/test_if_statements.py b/tests/test_if_statements.py new file mode 100644 index 0000000..9c00d90 --- /dev/null +++ b/tests/test_if_statements.py @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: MIT +# +# MIT License +# +# Copyright (c) 2024 Ericsson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +import pytest +from plantuml_gui.classes import PolyElement +from plantuml_gui.if_statements import get_line_for_adding_into_if + + +def test_get_if_index_raises_exception_if_invalid_combination_of_arguments(): + with pytest.raises(ValueError): + get_line_for_adding_into_if("plantuml", "svg", PolyElement("points"), "left")