完成世界书、骰子、apiconfig页面处理
This commit is contained in:
29
frontend/node_modules/cytoscape/.github/PULL_REQUEST_TEMPLATE.md
generated
vendored
Normal file
29
frontend/node_modules/cytoscape/.github/PULL_REQUEST_TEMPLATE.md
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
**Cross-references to related issues.** If there is no existing issue that describes your bug or feature request, then [create an issue](https://github.com/cytoscape/cytoscape.js/issues/new/choose) before making your pull request.
|
||||
|
||||
Associated issues:
|
||||
|
||||
- #X
|
||||
- #Y
|
||||
- #Z
|
||||
|
||||
**Notes re. the content of the pull request.** Give context to reviewers or serve as a general record of the changes made. Add a screenshot or video to demonstrate your new feature, if possible.
|
||||
|
||||
- This PR adds this.
|
||||
- This PR does that.
|
||||
- This PR allows us to do some other thing.
|
||||
|
||||
**Checklist**
|
||||
|
||||
Author:
|
||||
|
||||
- [ ] The proper base branch has been selected. New features go on `unstable`. Bug-fix patches can go on either `unstable` or `master`.
|
||||
- [ ] Automated tests have been included in this pull request, if possible, for the new feature(s) or bug fix. Check this box if tests are not pragmatically possible (e.g. rendering features could include screenshots or videos instead of automated tests).
|
||||
- [ ] The associated GitHub issues are included (above).
|
||||
- [ ] Notes have been included (above).
|
||||
- [ ] For new or updated API, the `index.d.ts` Typescript definition file has been appropriately updated.
|
||||
|
||||
Reviewers:
|
||||
|
||||
- [ ] All automated checks are passing (green check next to latest commit).
|
||||
- [ ] At least one reviewer has signed off on the pull request.
|
||||
- [ ] For bug fixes: Just after this pull request is merged, it should be applied to both the `master` branch and the `unstable` branch. Normally, this just requires cherry-picking the corresponding merge commit from `master` to `unstable` -- or vice versa.
|
||||
108
frontend/node_modules/cytoscape/.github/workflows/md/Patch_Backport_Release.md
generated
vendored
Normal file
108
frontend/node_modules/cytoscape/.github/workflows/md/Patch_Backport_Release.md
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
# Patch/Backport Release GitHub Action - README
|
||||
|
||||
## Introduction
|
||||
|
||||
The "Patch Release Test" GitHub Action automates the process of creating a patch release for the [Cytoscape.js](https://github.com/cytoscape/cytoscape.js) repository. This action allows you to define the target branch for the patch release and performs various tasks, including version updating, testing, publishing to npmjs and GitHub Releases, deploying to GitHub Pages, and more.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before using the "Patch Release Test" GitHub Action, ensure you have the following prerequisites:
|
||||
|
||||
1. Access to the [Cytoscape.js](https://github.com/cytoscape/cytoscape.js) repository.
|
||||
2. Necessary access tokens and secrets stored as GitHub repository secrets:
|
||||
- `NPM_TOKEN`: Token for npmjs package publishing. Ref: [How to create legacy token in npm](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-legacy-tokens-on-the-website)
|
||||
- `MAIN_GH_TOKEN`: Token for accessing GitHub API to publish GitHub Releases on Cytoscape/Cytoscape.js repo. Ref: [Create fine-grained-personal-access-tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#fine-grained-personal-access-tokens)
|
||||
|
||||
## Usage
|
||||
|
||||
1. Navigate to the [Cytoscape.js](https://github.com/cytoscape/cytoscape.js) repository.
|
||||
2. Go to the "Actions" tab.
|
||||
3. Click on the "Patch Release Test" workflow.
|
||||
4. Click the "Run workflow" button.
|
||||
5. Provide the target branch name for the patch release when prompted.
|
||||
6. For backport release: Make a corresponding patch release.
|
||||
|
||||
## Workflow Steps
|
||||
|
||||
The "Patch Release Test" GitHub Action comprises the following steps:
|
||||
|
||||
1. **Get Branch**:
|
||||
- Action: Retrieves the target branch for the patch release.
|
||||
- Script: Sets the `BRANCH` environment variable based on user input.
|
||||
- Uses the `github.event.inputs.branch` input for branch selection.
|
||||
|
||||
2. **Checkout Patch Branch**:
|
||||
- Action: Checks out the specified patch branch.
|
||||
- Uses: `actions/checkout@v3`.
|
||||
- Branch: The branch specified by the `BRANCH` environment variable.
|
||||
|
||||
3. **Setup Node.js Environment**:
|
||||
- Action: Sets up Node.js environment for the workflow.
|
||||
- Uses: `actions/setup-node@v3`.
|
||||
- Node Version: 18.
|
||||
- Caches npm packages.
|
||||
|
||||
4. **Get New Version String**:
|
||||
- Action: Determines the new version for the patch release.
|
||||
- Script: Retrieves the new version from a script.
|
||||
- Uses a custom script to calculate the new version.
|
||||
|
||||
5. **See Patch Branch**:
|
||||
- Action: Displays the selected branch for the patch release.
|
||||
- Command: Outputs the branch stored in the `BRANCH` environment variable.
|
||||
|
||||
6. **See New Patch Version**:
|
||||
- Action: Displays the calculated version for the patch release.
|
||||
- Command: Outputs the calculated version using the `VERSION` environment variable.
|
||||
|
||||
7. **Checkout Master Branch**:
|
||||
- Action: Checks out the `master` branch.
|
||||
- Uses: `actions/checkout@v3`.
|
||||
|
||||
8. **Update Version on Master**:
|
||||
- Action: Updates the `versions.json` file on the `master` branch.
|
||||
- Script: Uses `jq` to add the new version to the `versions.json` file.
|
||||
- Commits and pushes the updated `versions.json` file.
|
||||
|
||||
9. **Checkout Patch Branch Again**:
|
||||
- Action: Checks out the specified patch branch.
|
||||
- Uses: `actions/checkout@v3`.
|
||||
- Branch: The branch specified by the `BRANCH` environment variable.
|
||||
|
||||
10. **Update Version on Unstable**:
|
||||
- Action: Updates the `versions.json` file on the `unstable` branch.
|
||||
- Script: Uses `jq` to add the new version to the `versions.json` file.
|
||||
- Commits and pushes the updated `versions.json` file.
|
||||
- Checks out the original patch branch again.
|
||||
|
||||
11. **Install Dependencies**:
|
||||
- Action: Installs project dependencies.
|
||||
- Command: `npm install`.
|
||||
|
||||
12. **Run Tests**:
|
||||
- Action: Executes tests for the project.
|
||||
- Command: `npm test`.
|
||||
|
||||
13. **Set Git Config**:
|
||||
- Action: Configures Git with user information.
|
||||
- Sets the user name and email based on the GitHub actor.
|
||||
|
||||
14. **Pre Release Tests**:
|
||||
- Action: Executes pre-release tests.
|
||||
- Uses a custom script to run pre-release tests on the specified branch.
|
||||
|
||||
15. **Archive Code Coverage Results**:
|
||||
- Action: Archives code coverage results in case of test failure.
|
||||
- Uses: `actions/upload-artifact@v3`.
|
||||
|
||||
16. **Publish Package to npmjs**:
|
||||
- Action: Publishes the package to npmjs.
|
||||
- Command: `npm publish`.
|
||||
|
||||
17. **Publish Package to GitHub Releases**:
|
||||
- Action: Publishes the package to GitHub Releases.
|
||||
- Uses GitHub API to create a release with provided information.
|
||||
|
||||
18. **Deploy to Github Pages**:
|
||||
- Action: Deploys documentation to GitHub Pages.
|
||||
- Uses: `JamesIves/github-pages-deploy-action@v4`.
|
||||
115
frontend/node_modules/cytoscape/.github/workflows/patch-release.yml
generated
vendored
Normal file
115
frontend/node_modules/cytoscape/.github/workflows/patch-release.yml
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
name: Patch Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: 'Branch to run the action on'
|
||||
required: true
|
||||
default: 'master'
|
||||
|
||||
jobs:
|
||||
patch-release:
|
||||
runs-on: ubuntu-latest
|
||||
environment: prod
|
||||
steps:
|
||||
- name: Extract branch name
|
||||
shell: bash
|
||||
id: extract_branch
|
||||
run: |
|
||||
echo "BRANCH=${{ github.event.inputs.branch }}" >> $GITHUB_ENV
|
||||
echo "Branch: " $BRANCH
|
||||
- name: checkout patch branch
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ env.BRANCH }}
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 22
|
||||
cache: 'npm'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
- name: Run tests
|
||||
run : |
|
||||
npm test
|
||||
- name: Get new version string
|
||||
id: get_new_patch_version
|
||||
run: |
|
||||
chmod +rx ./.github/workflows/scripts/new-patch-version.sh
|
||||
. ./.github/workflows/scripts/new-patch-version.sh
|
||||
shell: bash
|
||||
- name: See patch branch
|
||||
run: echo branch ${{ env.BRANCH }}
|
||||
shell: bash
|
||||
- name: See new patch version
|
||||
run: echo "# version:" ${{ env.VERSION }}
|
||||
shell: bash
|
||||
- name: checkout master branch
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: master
|
||||
fetch-depth: 0
|
||||
- name: Set Git Config
|
||||
run : |
|
||||
# Set git configs
|
||||
git config --global user.name "${GITHUB_ACTOR}"
|
||||
git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com"
|
||||
- name: Update Version on master
|
||||
id: update_backport_version_master
|
||||
run: |
|
||||
jq --arg ver "${{ env.VERSION }}" '.versions += [$ver]' ./documentation/versions.json >> /tmp/temp.json
|
||||
mv /tmp/temp.json ./documentation/versions.json
|
||||
|
||||
git add . && git commit -m "Docs: Add ${{ env.VERSION }} to versions.json"
|
||||
git push
|
||||
- name: checkout unstable branch
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: unstable
|
||||
fetch-depth: 0
|
||||
- name: Update Version on unstable
|
||||
id: update_backport_version_unstable
|
||||
run: |
|
||||
jq --arg ver "${{ env.VERSION }}" '.versions += [$ver]' ./documentation/versions.json >> /tmp/temp.json
|
||||
mv /tmp/temp.json ./documentation/versions.json
|
||||
|
||||
git add . && git commit -m "Docs: Add ${{ env.VERSION }} to versions.json"
|
||||
git push
|
||||
git checkout ${{ env.BRANCH }}
|
||||
- name: Set Git Config
|
||||
run : |
|
||||
# Set git configs
|
||||
git config --global user.name "${GITHUB_ACTOR}"
|
||||
git config --global user.email "${GITHUB_ACTOR}@users.noreply.github.com"
|
||||
- name: Make Release
|
||||
id: release
|
||||
run: |
|
||||
chmod +rx .github/workflows/scripts/pre_release_test.sh
|
||||
. .github/workflows/scripts/pre_release_test.sh ${{ env.BRANCH }}
|
||||
- name: Archive action failure results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ failure() && steps.release.conclusion == 'failure' }}
|
||||
with:
|
||||
name: npm-release--failure-report
|
||||
path: /home/runner/.npm/_logs/
|
||||
- name: Publish Package To npmjs
|
||||
run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Publish Package to GitHub Releases
|
||||
run: |
|
||||
curl -L \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.MAIN_GH_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${{ github.repository }}/releases \
|
||||
-d '{"tag_name":"v${{ env.VERSION }}","target_commitish":"master","name":"v${{ env.VERSION }}","body":"Release version v${{ env.VERSION }}","draft":false,"prerelease":false,"generate_release_notes":false}'
|
||||
- name: Deploy to Github Pages 🚀
|
||||
if: ${{ env.BRANCH == 'master' }}
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
with:
|
||||
folder: documentation
|
||||
173
frontend/node_modules/cytoscape/README.md
generated
vendored
Normal file
173
frontend/node_modules/cytoscape/README.md
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
<img style="width: 200px; height: 200px;" src="https://raw.githubusercontent.com/cytoscape/cytoscape.js/unstable/documentation/img/cytoscape-logo.png" width="200" height="200">
|
||||
|
||||
[](https://github.com/cytoscape/cytoscape.js)
|
||||
[](https://www.phind.com/search?q=I%27m%20using%20the%20Cytoscape.js%20graph%20theory%20JS%20library.%20How%20do%20I%20create%20a%20graph%20in%20my%20HTML%20page)
|
||||
[](https://blog.js.cytoscape.org)
|
||||
[](https://raw.githubusercontent.com/cytoscape/cytoscape.js/master/LICENSE)
|
||||
[](https://www.npmjs.com/package/cytoscape)
|
||||
[](https://zenodo.org/badge/latestdoi/2255947)
|
||||
[](https://www.npmjs.com/package/cytoscape)
|
||||
[](https://github.com/cytoscape/cytoscape.js/actions/workflows/tests.yml)
|
||||
[](https://js.cytoscape.org/#extensions)
|
||||
[](https://cloudflare.com)
|
||||
|
||||
|
||||
Created at the [University of Toronto](https://utoronto.ca) and published in [Oxford Bioinformatics](https://js.cytoscape.org/#introduction/citation) ([2016](https://academic.oup.com/bioinformatics/article/32/2/309/1744007), [2023](https://academic.oup.com/bioinformatics/article/39/1/btad031/6988031)). <br />
|
||||
Authored by: [Max Franz](https://github.com/maxkfranz), [Christian Lopes](https://github.com/chrtannus), [Dylan Fong](https://github.com/d2fong), [Mike Kucera](https://github.com/mikekucera), ..., [Gary Bader](https://baderlab.org)
|
||||
|
||||
# Cytoscape.js
|
||||
|
||||
Graph theory (network) library for visualisation and analysis : [https://js.cytoscape.org](https://js.cytoscape.org)
|
||||
|
||||
## Description
|
||||
|
||||
Cytoscape.js is a fully featured [graph theory](https://en.wikipedia.org/wiki/Graph_theory) library. Do you need to model and/or visualise relational data, like biological data or social networks? If so, Cytoscape.js is just what you need.
|
||||
|
||||
Cytoscape.js contains a graph theory model and an optional renderer to display interactive graphs. This library was designed to make it as easy as possible for programmers and scientists to use graph theory in their apps, whether it's for server-side analysis in a Node.js app or for a rich user interface.
|
||||
|
||||
You can get started with Cytoscape.js with one line:
|
||||
|
||||
```js
|
||||
var cy = cytoscape({ elements: myElements, container: myDiv });
|
||||
```
|
||||
|
||||
Learn more about the features of Cytoscape.js by reading [its documentation](https://js.cytoscape.org).
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
The Tokyo railway stations network can be visualised with Cytoscape:
|
||||
|
||||
<img style="width: 300px; height: 126px;" src="https://raw.githubusercontent.com/cytoscape/cytoscape.js/unstable/documentation/img/tokyo-big.png" width="300" height="126">
|
||||
|
||||
<img style="width: 300px; height: 126px;" src="https://raw.githubusercontent.com/cytoscape/cytoscape.js/unstable/documentation/img/tokyo-big-zoomed-in.png" width="300" height="126">
|
||||
|
||||
A [live demo](https://js.cytoscape.org/demos/tokyo-railways/) and [source code](https://github.com/cytoscape/cytoscape.js/tree/master/documentation/demos/tokyo-railways) are available for the Tokyo railway stations graph. More demos are available in the [documentation](https://js.cytoscape.org/#demos).
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find the documentation and downloads on the [project website](https://js.cytoscape.org).
|
||||
|
||||
|
||||
|
||||
## Roadmap
|
||||
|
||||
Future versions of Cytoscape.js are planned in the [milestones of the Github issue tracker](https://github.com/cytoscape/cytoscape.js/milestones). You can use the milestones to see what's currently planned for future releases.
|
||||
|
||||
|
||||
|
||||
|
||||
## Contributing to Cytoscape.js
|
||||
|
||||
Would you like to become a Cytoscape.js contributor? You can contribute in technical roles (e.g. features, testing) or non-technical roles (e.g. documentation, outreach), depending on your interests. [Get in touch with us by posting a GitHub discussion](https://github.com/cytoscape/cytoscape.js/discussions).
|
||||
|
||||
For the mechanics of contributing a pull request, refer to [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
|
||||
Feature releases are made monthly, while patch releases are made weekly. This allows for rapid releases of first- and third-party contributions.
|
||||
|
||||
|
||||
|
||||
## Citation
|
||||
|
||||
To cite Cytoscape.js in a paper, please cite the Oxford Bioinformatics issue:
|
||||
|
||||
*Cytoscape.js: a graph theory library for visualisation and analysis*
|
||||
|
||||
Franz M, Lopes CT, Huck G, Dong Y, Sumer O, Bader GD
|
||||
|
||||
[Bioinformatics (2016) 32 (2): 309-311 first published online September 28, 2015 doi:10.1093/bioinformatics/btv557](https://bioinformatics.oxfordjournals.org/content/32/2/309) [(PDF)](http://bioinformatics.oxfordjournals.org/content/32/2/309.full.pdf)
|
||||
|
||||
- [PubMed abstract for the original 2016 article](http://www.ncbi.nlm.nih.gov/pubmed/26415722)
|
||||
- [PubMed abstract for the 2023 update article](https://pubmed.ncbi.nlm.nih.gov/36645249)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Build dependencies
|
||||
|
||||
Install `node` and `npm`. Run `npm install` before using `npm run`.
|
||||
|
||||
|
||||
|
||||
|
||||
## Build instructions
|
||||
|
||||
Run `npm run <target>` in the console. The main targets are:
|
||||
|
||||
**Building:**
|
||||
|
||||
* `build`: do all builds of the library (umd, min, umd, esm)
|
||||
* `build:min` : do the unminified build with bundled dependencies (for simple html pages, good for novices)
|
||||
* `build:umd` : do the umd (cjs/amd/globals) build
|
||||
* `build:esm` : do the esm (ES 2015 modules) build
|
||||
* `clean` : clean the `build` directory
|
||||
* `docs` : build the docs into `documentation`
|
||||
* `release` : build all release artifacts
|
||||
* `watch` : automatically build lib for debugging (with sourcemap, no babel, very quick)
|
||||
* good for general testing on `debug/index.html`
|
||||
* served on `http://localhost:8080` or the first available port thereafter, with livereload on `debug/index.html`
|
||||
* `watch:babel` : automatically build lib for debugging (with sourcemap, with babel, a bit slower)
|
||||
* good for testing performance or for testing out of date browsers
|
||||
* served on `http://localhost:8080` or the first available port thereafter, with livereload on `debug/index.html`
|
||||
* `watch:umd` : automatically build prod umd bundle (no sourcemap, with babel)
|
||||
* good for testing cytoscape in another project (with a `"cytoscape": "file:./path/to/cytoscape"` reference in your project's `package.json`)
|
||||
* no http server
|
||||
* `dist` : update the distribution js for npm etc.
|
||||
|
||||
**Testing:**
|
||||
|
||||
The default test scripts run directly against the source code. Tests can alternatively be run on a built bundle. The library can be built on `node>=6`, but the library's bundle can be tested on `node>=0.10`.
|
||||
|
||||
* `test` : run all testing & linting
|
||||
* `test:js` : run the mocha tests on the public API of the lib (directly on source files)
|
||||
* `npm run test:js -- -g "my test name"` runs tests on only the matching test cases
|
||||
* `test:build` : run the mocha tests on the public API of the lib (on a built bundle)
|
||||
* `npm run build` should be run beforehand on a recent version of node
|
||||
* `npm run test:build -- -g "my test name"` runs build tests on only the matching test cases
|
||||
* `test:modules` : run unit tests on private, internal API
|
||||
* `npm run test:modules -- -g "my test name"` runs modules tests on only the matching test cases
|
||||
* `lint` : lint the js sources via eslint
|
||||
* `benchmark` : run all benchmarks
|
||||
* `benchmark:single` : run benchmarks only for the suite specified in `benchmark/single`
|
||||
|
||||
|
||||
|
||||
## Release instructions
|
||||
|
||||
### Background
|
||||
|
||||
- Ensure that a milestone exists for the release you want to make, with all the issues for that release assigned in the milestone.
|
||||
- Bug fixes should be applied to both the `master` and `unstable` branches. PRs can go on either branch, with the patch applied to the other branch after merging.
|
||||
- When a patch release is made concurrently with a feature release, the patch release should be made first. Wait 5 minutes after the patch release completes before starting the feature release -- otherwise Zenodo doesn't pick up releases properly.
|
||||
|
||||
### Patch version
|
||||
|
||||
1. Go to [Actions > Patch release](https://github.com/cytoscape/cytoscape.js/actions/workflows/patch-release.yml)
|
||||
1. Go to the 'Run workflow' dropdown
|
||||
1. [Optional] The 'master' branch should be preselected for you
|
||||
1. Press the green 'Run workflow' button
|
||||
1. Close the milestone for the release
|
||||
|
||||
<img style="width: 300px; height: auto;" src="https://raw.githubusercontent.com/cytoscape/cytoscape.js/unstable/documentation/img/preview-patch.png" width="300">
|
||||
|
||||
### Feature version
|
||||
|
||||
1. Go to [Actions > Feature release](https://github.com/cytoscape/cytoscape.js/actions/workflows/feature-release.yml)
|
||||
1. Go to the 'Run workflow' dropdown
|
||||
1. [Optional] The 'unstable' branch should be preselected for you
|
||||
1. Press the green 'Run workflow' button
|
||||
1. Close the milestone for the release
|
||||
1. Make the release announcement [on the blog](https://github.com/cytoscape/cytoscape.js-blog)
|
||||
|
||||
<img style="width: 300px; height: auto;" src="https://raw.githubusercontent.com/cytoscape/cytoscape.js/unstable/documentation/img/preview-feature.png" width="300">
|
||||
|
||||
### Notes on GitHub Actions UI
|
||||
|
||||
- 'Use workflow from' in the GitHub UI selects the branch from which the workflow YML file is selected. Since the workflow files should usually be the same on the master and unstable branches, it shouldn't matter what's selected.
|
||||
- 'Branch to run the action on' in the GitHub UI is preselected for you. You don't need to change it.
|
||||
|
||||
## Tests
|
||||
|
||||
Mocha tests are found in the [test directory](https://github.com/cytoscape/cytoscape.js/tree/master/test). The tests can be run in the browser or they can be run via Node.js (`npm run test:js`).
|
||||
9
frontend/node_modules/cytoscape/dist/cytoscape.esm.min.mjs
generated
vendored
Normal file
9
frontend/node_modules/cytoscape/dist/cytoscape.esm.min.mjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
35580
frontend/node_modules/cytoscape/dist/cytoscape.esm.mjs
generated
vendored
Normal file
35580
frontend/node_modules/cytoscape/dist/cytoscape.esm.mjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
143
frontend/node_modules/cytoscape/package.json
generated
vendored
Normal file
143
frontend/node_modules/cytoscape/package.json
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
{
|
||||
"name": "cytoscape",
|
||||
"version": "3.33.1",
|
||||
"license": "MIT",
|
||||
"description": "Graph theory (a.k.a. network) library for analysis and visualisation",
|
||||
"homepage": "http://js.cytoscape.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cytoscape/cytoscape.js.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/cytoscape/cytoscape.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"graph",
|
||||
"graph-theory",
|
||||
"network",
|
||||
"node",
|
||||
"edge",
|
||||
"vertex",
|
||||
"link",
|
||||
"analysis",
|
||||
"visualisation",
|
||||
"visualization",
|
||||
"draw",
|
||||
"render",
|
||||
"biojs",
|
||||
"cytoscape"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
},
|
||||
"types": "index.d.ts",
|
||||
"main": "dist/cytoscape.cjs.js",
|
||||
"module": "dist/cytoscape.esm.mjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/cytoscape.esm.mjs",
|
||||
"require": "./dist/cytoscape.cjs.js",
|
||||
"types": "./index.d.ts"
|
||||
},
|
||||
"./dist/cytoscape.esm.mjs": {
|
||||
"import": "./dist/cytoscape.esm.mjs"
|
||||
},
|
||||
"./dist/cytoscape.esm": {
|
||||
"import": "./dist/cytoscape.esm.mjs"
|
||||
},
|
||||
"./dist/cytoscape.esm.min.mjs": {
|
||||
"import": "./dist/cytoscape.esm.min.mjs"
|
||||
},
|
||||
"./dist/cytoscape.esm.min": {
|
||||
"import": "./dist/cytoscape.esm.min.mjs"
|
||||
},
|
||||
"./dist/cytoscape.cjs.js": {
|
||||
"require": "./dist/cytoscape.cjs.js"
|
||||
},
|
||||
"./dist/cytoscape.umd.js": {
|
||||
"require": "./dist/cytoscape.umd.js"
|
||||
},
|
||||
"./dist/cytoscape.min.js": {
|
||||
"require": "./dist/cytoscape.min.js"
|
||||
}
|
||||
},
|
||||
"unpkg": "dist/cytoscape.min.js",
|
||||
"jsdelivr": "dist/cytoscape.min.js",
|
||||
"scripts": {
|
||||
"lint": "eslint src/**/*.mjs",
|
||||
"build": "rollup -c",
|
||||
"build:esm": "cross-env FILE=esm rollup -c",
|
||||
"build:esm.min": "cross-env FILE=esm.min rollup -c",
|
||||
"build:cjs": "cross-env FILE=cjs rollup -c",
|
||||
"build:umd": "cross-env FILE=umd rollup -c",
|
||||
"build:min": "cross-env FILE=min rollup -c",
|
||||
"clean": "rimraf build/*",
|
||||
"copyright": "node license-update.mjs",
|
||||
"dist:copy": "cpy --flat build/cytoscape.umd.js build/cytoscape.min.js build/cytoscape.cjs.js build/cytoscape.esm.mjs build/cytoscape.esm.min.mjs dist",
|
||||
"dist": "cross-env NODE_ENV=production run-s build dist:*",
|
||||
"release": "run-s copyright dist docs",
|
||||
"watch": "run-s watch:fast",
|
||||
"watch:sync": "livereload \"build, debug\" -w 500",
|
||||
"watch:http": "http-server -p 3333 -s -c -1 -o debug",
|
||||
"watch:fast": "run-p watch:sync watch:http watch:build:fast",
|
||||
"watch:umd": "run-p watch:sync watch:http watch:build:umd",
|
||||
"watch:build:fast": "cross-env FILE=umd SOURCEMAPS=true BABEL=false NODE_ENV=development rollup -c -w",
|
||||
"watch:build:umd": "cross-env FILE=umd SOURCEMAPS=true NODE_ENV=development rollup -c -w",
|
||||
"watch:build:cjs": "cross-env FILE=cjs SOURCEMAPS=true NODE_ENV=development rollup -c -w",
|
||||
"test": "run-s test:js test:modules test:playwright lint",
|
||||
"test:js": "mocha --recursive",
|
||||
"test:js:debug": "mocha inspect --recursive",
|
||||
"test:build": "cross-env TEST_BUILD=true mocha",
|
||||
"test:modules": "mocha test/modules",
|
||||
"test:modules:debug": "mocha inspect test/modules",
|
||||
"test:playwright:http": "http-server -p 3333 -s -c -1",
|
||||
"test:playwright:build": "cross-env FILE=umd SOURCEMAPS=true BABEL=false NODE_ENV=development rollup -c",
|
||||
"test:playwright:setup": "run-s test:playwright:build test:playwright:http",
|
||||
"test:playwright": "playwright test",
|
||||
"docs": "run-s docs:build docs:js",
|
||||
"docs:js": "cpy --flat build/cytoscape.min.js documentation/js",
|
||||
"docs:build": "node documentation/docmaker.mjs",
|
||||
"docs:push": "gh-pages -d documentation",
|
||||
"benchmark": "run-s benchmark:all",
|
||||
"benchmark:download": "download https://raw.githubusercontent.com/cytoscape/cytoscape.js/master/dist/cytoscape.cjs.js --out build --filename cytoscape.benchmark.js",
|
||||
"benchmark:all:exec": "node benchmark/all",
|
||||
"benchmark:all": "run-s benchmark:download benchmark:all:exec",
|
||||
"benchmark:single:exec": "node benchmark/single",
|
||||
"benchmark:single": "run-s benchmark:download benchmark:single:exec"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/preset-env": "^7.26.0",
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@playwright/test": "^1.49.1",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-commonjs": "^28.0.2",
|
||||
"@rollup/plugin-node-resolve": "^16.0.0",
|
||||
"@rollup/plugin-replace": "^6.0.2",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@types/node": "^22.10.7",
|
||||
"benchmark": "^2.1.4",
|
||||
"bluebird": "^3.5.0",
|
||||
"chai": "^5.1.2",
|
||||
"cpy-cli": "^5.0.0",
|
||||
"cross-env": "^7.0.0",
|
||||
"download-cli": "^1.0.5",
|
||||
"eslint": "^9.18.0",
|
||||
"gh-pages": "^5.0.0",
|
||||
"gl-matrix": "^3.4.3",
|
||||
"globals": "^15.14.0",
|
||||
"handlebars": "^4.7.6",
|
||||
"heap": "^0.2.7",
|
||||
"highlight.js": "^10.0.0",
|
||||
"http-server": "^14.1.1",
|
||||
"jsonlint": "^1.6.2",
|
||||
"livereload": "^0.9.1",
|
||||
"lodash": "^4.17.21",
|
||||
"marked": "^4.0.10",
|
||||
"mocha": "^11.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"rimraf": "^3.0.0",
|
||||
"rollup": "^4.31.0",
|
||||
"rollup-plugin-license": "^3.5.3"
|
||||
}
|
||||
}
|
||||
329
frontend/node_modules/cytoscape/src/collection/algorithms/affinity-propagation.mjs
generated
vendored
Normal file
329
frontend/node_modules/cytoscape/src/collection/algorithms/affinity-propagation.mjs
generated
vendored
Normal file
@@ -0,0 +1,329 @@
|
||||
// Implemented by Zoe Xi @zoexi for GSOC 2016
|
||||
// https://github.com/cytoscape/cytoscape.js-affinity-propagation
|
||||
|
||||
// Implemented from the reference library: https://github.com/juhis/affinity-propagation
|
||||
// Additional reference: http://www.psi.toronto.edu/affinitypropagation/faq.html
|
||||
|
||||
import * as util from '../../util/index.mjs';
|
||||
import * as math from '../../math.mjs';
|
||||
import * as is from '../../is.mjs';
|
||||
import clusteringDistance from './clustering-distances.mjs';
|
||||
|
||||
let defaults = util.defaults({
|
||||
distance: 'euclidean', // distance metric to compare attributes between two nodes
|
||||
preference: 'median', // suitability of a data point to serve as an exemplar
|
||||
damping: 0.8, // damping factor between [0.5, 1)
|
||||
maxIterations: 1000, // max number of iterations to run
|
||||
minIterations: 100, // min number of iterations to run in order for clustering to stop
|
||||
attributes: [ // functions to quantify the similarity between any two points
|
||||
// e.g. node => node.data('weight')
|
||||
]
|
||||
});
|
||||
|
||||
let setOptions = function( options ) {
|
||||
let dmp = options.damping;
|
||||
let pref = options.preference;
|
||||
|
||||
if( !(0.5 <= dmp && dmp < 1) ){
|
||||
util.error(`Damping must range on [0.5, 1). Got: ${dmp}`);
|
||||
}
|
||||
|
||||
let validPrefs = ['median', 'mean', 'min', 'max'];
|
||||
if( !( validPrefs.some(v => v === pref) || is.number(pref) ) ){
|
||||
util.error(`Preference must be one of [${validPrefs.map( p => `'${p}'` ).join(', ')}] or a number. Got: ${pref}`);
|
||||
}
|
||||
|
||||
return defaults( options );
|
||||
};
|
||||
|
||||
if( process.env.NODE_ENV !== 'production' ){ /* eslint-disable no-console, no-unused-vars */
|
||||
var printMatrix = function( M ) { // used for debugging purposes only
|
||||
let str = '';
|
||||
let log = s => str = str + s + '\n';
|
||||
let n = Math.sqrt(M.length);
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
let row = '';
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
row += M[i*n+j] + ' ';
|
||||
}
|
||||
log(row);
|
||||
}
|
||||
|
||||
console.log(str);
|
||||
};
|
||||
} /* eslint-enable */
|
||||
|
||||
let getSimilarity = function( type, n1, n2, attributes ) {
|
||||
let attr = (n, i) => attributes[i](n);
|
||||
|
||||
// nb negative because similarity should have an inverse relationship to distance
|
||||
return -clusteringDistance( type, attributes.length, i => attr(n1, i), i => attr(n2, i), n1, n2 );
|
||||
};
|
||||
|
||||
let getPreference = function( S, preference ) { // larger preference = greater # of clusters
|
||||
let p = null;
|
||||
|
||||
if( preference === 'median' ){
|
||||
p = math.median( S );
|
||||
} else if( preference === 'mean' ){
|
||||
p = math.mean( S );
|
||||
} else if ( preference === 'min' ){
|
||||
p = math.min( S );
|
||||
} else if ( preference === 'max' ){
|
||||
p = math.max( S );
|
||||
} else { // Custom preference number, as set by user
|
||||
p = preference;
|
||||
}
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
let findExemplars = function( n, R, A ) {
|
||||
let indices = [];
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
if ( R[i * n + i] + A[i * n + i] > 0 ) {
|
||||
indices.push(i);
|
||||
}
|
||||
}
|
||||
return indices;
|
||||
};
|
||||
|
||||
let assignClusters = function( n, S, exemplars ) {
|
||||
let clusters = [];
|
||||
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
let index = -1;
|
||||
let max = -Infinity;
|
||||
|
||||
for ( let ei = 0; ei < exemplars.length; ei++ ) {
|
||||
let e = exemplars[ei];
|
||||
if ( S[i * n + e] > max ) {
|
||||
index = e;
|
||||
max = S[i * n + e];
|
||||
}
|
||||
}
|
||||
|
||||
if( index > 0 ){
|
||||
clusters.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
for (let ei = 0; ei < exemplars.length; ei++) {
|
||||
clusters[ exemplars[ei] ] = exemplars[ei];
|
||||
}
|
||||
|
||||
return clusters;
|
||||
};
|
||||
|
||||
let assign = function( n, S, exemplars ) {
|
||||
|
||||
let clusters = assignClusters( n, S, exemplars );
|
||||
|
||||
for ( let ei = 0; ei < exemplars.length; ei++ ) {
|
||||
|
||||
let ii = [];
|
||||
for ( let c = 0; c < clusters.length; c++ ) {
|
||||
if (clusters[c] === exemplars[ei]) {
|
||||
ii.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
let maxI = -1;
|
||||
let maxSum = -Infinity;
|
||||
for ( let i = 0; i < ii.length; i++ ) {
|
||||
let sum = 0;
|
||||
for ( let j = 0; j < ii.length; j++ ) {
|
||||
sum += S[ii[j] * n + ii[i]];
|
||||
}
|
||||
if ( sum > maxSum ) {
|
||||
maxI = i;
|
||||
maxSum = sum;
|
||||
}
|
||||
}
|
||||
|
||||
exemplars[ei] = ii[maxI];
|
||||
}
|
||||
|
||||
clusters = assignClusters( n, S, exemplars );
|
||||
|
||||
return clusters;
|
||||
};
|
||||
|
||||
let affinityPropagation = function( options ) {
|
||||
let cy = this.cy();
|
||||
let nodes = this.nodes();
|
||||
let opts = setOptions( options );
|
||||
|
||||
// Map each node to its position in node array
|
||||
let id2position = {};
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
id2position[ nodes[i].id() ] = i;
|
||||
}
|
||||
|
||||
// Begin affinity propagation algorithm
|
||||
|
||||
let n; // number of data points
|
||||
let n2; // size of matrices
|
||||
let S; // similarity matrix (1D array)
|
||||
let p; // preference/suitability of a data point to serve as an exemplar
|
||||
let R; // responsibility matrix (1D array)
|
||||
let A; // availability matrix (1D array)
|
||||
|
||||
n = nodes.length;
|
||||
n2 = n * n;
|
||||
|
||||
// Initialize and build S similarity matrix
|
||||
S = new Array(n2);
|
||||
for ( let i = 0; i < n2; i++ ) {
|
||||
S[i] = -Infinity; // for cases where two data points shouldn't be linked together
|
||||
}
|
||||
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
if ( i !== j ) {
|
||||
S[i * n + j] = getSimilarity( opts.distance, nodes[i], nodes[j], opts.attributes );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Place preferences on the diagonal of S
|
||||
p = getPreference( S, opts.preference );
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
S[i * n + i] = p;
|
||||
}
|
||||
|
||||
// Initialize R responsibility matrix
|
||||
R = new Array(n2);
|
||||
for ( let i = 0; i < n2; i++ ) {
|
||||
R[i] = 0.0;
|
||||
}
|
||||
|
||||
// Initialize A availability matrix
|
||||
A = new Array(n2);
|
||||
for ( let i = 0; i < n2; i++ ) {
|
||||
A[i] = 0.0;
|
||||
}
|
||||
|
||||
let old = new Array(n);
|
||||
let Rp = new Array(n);
|
||||
let se = new Array(n);
|
||||
|
||||
for ( let i = 0; i < n; i ++ ) {
|
||||
old[i] = 0.0;
|
||||
Rp[i] = 0.0;
|
||||
se[i] = 0;
|
||||
}
|
||||
|
||||
let e = new Array(n * opts.minIterations);
|
||||
for ( let i = 0; i < e.length; i++ ) {
|
||||
e[i] = 0;
|
||||
}
|
||||
|
||||
let iter;
|
||||
for ( iter = 0; iter < opts.maxIterations; iter++ ) { // main algorithmic loop
|
||||
|
||||
// Update R responsibility matrix
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
|
||||
let max = -Infinity,
|
||||
max2 = -Infinity,
|
||||
maxI = -1,
|
||||
AS = 0.0;
|
||||
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
|
||||
old[j] = R[i * n + j];
|
||||
|
||||
AS = A[i * n + j] + S[i * n + j];
|
||||
if ( AS >= max ) {
|
||||
max2 = max;
|
||||
max = AS;
|
||||
maxI = j;
|
||||
}
|
||||
else if ( AS > max2 ) {
|
||||
max2 = AS;
|
||||
}
|
||||
}
|
||||
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
R[i * n + j] = (1 - opts.damping) * (S[i * n + j] - max) + opts.damping * old[j];
|
||||
}
|
||||
|
||||
R[i * n + maxI] = (1 - opts.damping) * (S[i * n + maxI] - max2) + opts.damping * old[maxI];
|
||||
}
|
||||
|
||||
// Update A availability matrix
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
let sum = 0;
|
||||
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
old[j] = A[j * n + i];
|
||||
Rp[j] = Math.max(0, R[j * n + i]);
|
||||
sum += Rp[j];
|
||||
}
|
||||
|
||||
sum -= Rp[i];
|
||||
Rp[i] = R[i * n + i];
|
||||
sum += Rp[i];
|
||||
|
||||
for ( let j = 0; j < n; j++ ) {
|
||||
A[j * n + i] = (1 - opts.damping) * Math.min(0, sum - Rp[j]) + opts.damping * old[j];
|
||||
}
|
||||
A[i * n + i] = (1 - opts.damping) * (sum - Rp[i]) + opts.damping * old[i];
|
||||
}
|
||||
|
||||
// Check for convergence
|
||||
let K = 0;
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
let E = A[i * n + i] + R[i * n + i] > 0 ? 1 : 0;
|
||||
e[(iter % opts.minIterations) * n + i] = E;
|
||||
K += E;
|
||||
}
|
||||
|
||||
if ( K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1) ) {
|
||||
|
||||
let sum = 0;
|
||||
for ( let i = 0; i < n; i++ ) {
|
||||
se[i] = 0;
|
||||
for ( let j = 0; j < opts.minIterations; j++ ) {
|
||||
se[i] += e[j * n + i];
|
||||
}
|
||||
if ( se[i] === 0 || se[i] === opts.minIterations ) {
|
||||
sum++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( sum === n ) { // then we have convergence
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Identify exemplars (cluster centers)
|
||||
let exemplarsIndices = findExemplars( n, R, A );
|
||||
|
||||
// Assign nodes to clusters
|
||||
let clusterIndices = assign( n, S, exemplarsIndices, nodes, id2position );
|
||||
|
||||
let clusters = {};
|
||||
for ( let c = 0; c < exemplarsIndices.length; c++ ) {
|
||||
clusters[ exemplarsIndices[c] ] = [];
|
||||
}
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
let pos = id2position[ nodes[i].id() ];
|
||||
let clusterIndex = clusterIndices[pos];
|
||||
|
||||
if( clusterIndex != null ){ // the node may have not been assigned a cluster if no valid attributes were specified
|
||||
clusters[ clusterIndex ].push( nodes[i] );
|
||||
}
|
||||
}
|
||||
let retClusters = new Array(exemplarsIndices.length);
|
||||
for ( let c = 0; c < exemplarsIndices.length; c++ ) {
|
||||
retClusters[c] = cy.collection( clusters[ exemplarsIndices[c] ] );
|
||||
}
|
||||
|
||||
return retClusters;
|
||||
};
|
||||
|
||||
export default { affinityPropagation, ap: affinityPropagation };
|
||||
174
frontend/node_modules/cytoscape/src/collection/algorithms/betweenness-centrality.mjs
generated
vendored
Normal file
174
frontend/node_modules/cytoscape/src/collection/algorithms/betweenness-centrality.mjs
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
import Heap from '../../heap.mjs';
|
||||
import * as util from '../../util/index.mjs';
|
||||
|
||||
const defaults = util.defaults({
|
||||
weight: null,
|
||||
directed: false
|
||||
});
|
||||
|
||||
let elesfn = ({
|
||||
|
||||
// Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
|
||||
betweennessCentrality: function( options ){
|
||||
let { directed, weight } = defaults(options);
|
||||
let weighted = weight != null;
|
||||
let cy = this.cy();
|
||||
|
||||
// starting
|
||||
let V = this.nodes();
|
||||
let A = {};
|
||||
let _C = {};
|
||||
let max = 0;
|
||||
let C = {
|
||||
set: function( key, val ){
|
||||
_C[ key ] = val;
|
||||
|
||||
if( val > max ){ max = val; }
|
||||
},
|
||||
|
||||
get: function( key ){ return _C[ key ]; }
|
||||
};
|
||||
|
||||
// A contains the neighborhoods of every node
|
||||
for( let i = 0; i < V.length; i++ ){
|
||||
let v = V[ i ];
|
||||
let vid = v.id();
|
||||
|
||||
if( directed ){
|
||||
A[ vid ] = v.outgoers().nodes(); // get outgoers of every node
|
||||
} else {
|
||||
A[ vid ] = v.openNeighborhood().nodes(); // get neighbors of every node
|
||||
}
|
||||
|
||||
C.set( vid, 0 );
|
||||
}
|
||||
|
||||
for( let s = 0; s < V.length; s++ ){
|
||||
let sid = V[s].id();
|
||||
let S = []; // stack
|
||||
let P = {};
|
||||
let g = {};
|
||||
let d = {};
|
||||
let Q = new Heap(function( a, b ){
|
||||
return d[a] - d[b];
|
||||
}); // queue
|
||||
|
||||
// init dictionaries
|
||||
for( let i = 0; i < V.length; i++ ){
|
||||
let vid = V[ i ].id();
|
||||
|
||||
P[ vid ] = [];
|
||||
g[ vid ] = 0;
|
||||
d[ vid ] = Infinity;
|
||||
}
|
||||
|
||||
g[ sid ] = 1; // sigma
|
||||
d[ sid ] = 0; // distance to s
|
||||
|
||||
Q.push( sid );
|
||||
|
||||
while( !Q.empty() ){
|
||||
let v = Q.pop();
|
||||
|
||||
S.push( v );
|
||||
|
||||
if( weighted ){
|
||||
for( let j = 0; j < A[v].length; j++ ){
|
||||
let w = A[v][j];
|
||||
let vEle = cy.getElementById( v );
|
||||
|
||||
let edge;
|
||||
if( vEle.edgesTo( w ).length > 0 ){
|
||||
edge = vEle.edgesTo( w )[0];
|
||||
} else {
|
||||
edge = w.edgesTo( vEle )[0];
|
||||
}
|
||||
|
||||
let edgeWeight = weight( edge );
|
||||
|
||||
w = w.id();
|
||||
|
||||
if( d[w] > d[v] + edgeWeight ){
|
||||
d[w] = d[v] + edgeWeight;
|
||||
|
||||
if( Q.nodes.indexOf( w ) < 0 ){ //if w is not in Q
|
||||
Q.push( w );
|
||||
} else { // update position if w is in Q
|
||||
Q.updateItem( w );
|
||||
}
|
||||
|
||||
g[w] = 0;
|
||||
P[w] = [];
|
||||
}
|
||||
|
||||
if( d[w] == d[v] + edgeWeight ){
|
||||
g[w] = g[w] + g[v];
|
||||
P[w].push( v );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for( let j = 0; j < A[v].length; j++ ){
|
||||
let w = A[v][j].id();
|
||||
|
||||
if( d[w] == Infinity ){
|
||||
Q.push( w );
|
||||
|
||||
d[w] = d[v] + 1;
|
||||
}
|
||||
|
||||
if( d[w] == d[v] + 1 ){
|
||||
g[w] = g[w] + g[v];
|
||||
P[w].push( v );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let e = {};
|
||||
for( let i = 0; i < V.length; i++ ){
|
||||
e[ V[ i ].id() ] = 0;
|
||||
}
|
||||
|
||||
while( S.length > 0 ){
|
||||
let w = S.pop();
|
||||
|
||||
for( let j = 0; j < P[w].length; j++ ){
|
||||
let v = P[w][j];
|
||||
|
||||
e[v] = e[v] + (g[v] / g[w]) * (1 + e[w]);
|
||||
}
|
||||
|
||||
if( w != V[s].id() ){
|
||||
C.set( w, C.get( w ) + e[w] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ret = {
|
||||
betweenness: function( node ){
|
||||
let id = cy.collection(node).id();
|
||||
|
||||
return C.get( id );
|
||||
},
|
||||
|
||||
betweennessNormalized: function( node ){
|
||||
if ( max == 0 ){ return 0; }
|
||||
|
||||
let id = cy.collection(node).id();
|
||||
|
||||
return C.get( id ) / max;
|
||||
}
|
||||
};
|
||||
|
||||
// alias
|
||||
ret.betweennessNormalised = ret.betweennessNormalized;
|
||||
|
||||
return ret;
|
||||
} // betweennessCentrality
|
||||
|
||||
}); // elesfn
|
||||
|
||||
// nice, short mathematical alias
|
||||
elesfn.bc = elesfn.betweennessCentrality;
|
||||
|
||||
export default elesfn;
|
||||
101
frontend/node_modules/cytoscape/src/collection/algorithms/closeness-centrality.mjs
generated
vendored
Normal file
101
frontend/node_modules/cytoscape/src/collection/algorithms/closeness-centrality.mjs
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
import * as is from '../../is.mjs';
|
||||
import * as util from '../../util/index.mjs';
|
||||
|
||||
const defaults = util.defaults({
|
||||
harmonic: true,
|
||||
weight: () => 1,
|
||||
directed: false,
|
||||
root: null
|
||||
});
|
||||
|
||||
const elesfn = ({
|
||||
|
||||
closenessCentralityNormalized: function( options ){
|
||||
let { harmonic, weight, directed } = defaults(options);
|
||||
|
||||
let cy = this.cy();
|
||||
let closenesses = {};
|
||||
let maxCloseness = 0;
|
||||
let nodes = this.nodes();
|
||||
let fw = this.floydWarshall({ weight, directed });
|
||||
|
||||
// Compute closeness for every node and find the maximum closeness
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let currCloseness = 0;
|
||||
let node_i = nodes[i];
|
||||
|
||||
for( let j = 0; j < nodes.length; j++ ){
|
||||
if( i !== j ){
|
||||
let d = fw.distance( node_i, nodes[j] );
|
||||
|
||||
if( harmonic ){
|
||||
currCloseness += 1 / d;
|
||||
} else {
|
||||
currCloseness += d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !harmonic ){
|
||||
currCloseness = 1 / currCloseness;
|
||||
}
|
||||
|
||||
if( maxCloseness < currCloseness ){
|
||||
maxCloseness = currCloseness;
|
||||
}
|
||||
|
||||
closenesses[ node_i.id() ] = currCloseness;
|
||||
}
|
||||
|
||||
return {
|
||||
closeness: function( node ){
|
||||
if( maxCloseness == 0 ){ return 0; }
|
||||
|
||||
if( is.string( node ) ){
|
||||
// from is a selector string
|
||||
node = (cy.filter( node )[0]).id();
|
||||
} else {
|
||||
// from is a node
|
||||
node = node.id();
|
||||
}
|
||||
|
||||
return closenesses[ node ] / maxCloseness;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// Implemented from pseudocode from wikipedia
|
||||
closenessCentrality: function( options ){
|
||||
let { root, weight, directed, harmonic } = defaults(options);
|
||||
|
||||
root = this.filter(root)[0];
|
||||
|
||||
// we need distance from this node to every other node
|
||||
let dijkstra = this.dijkstra({ root, weight, directed });
|
||||
let totalDistance = 0;
|
||||
let nodes = this.nodes();
|
||||
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let n = nodes[i];
|
||||
|
||||
if( !n.same(root) ){
|
||||
let d = dijkstra.distanceTo(n);
|
||||
|
||||
if( harmonic ){
|
||||
totalDistance += 1 / d;
|
||||
} else {
|
||||
totalDistance += d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return harmonic ? totalDistance : 1 / totalDistance;
|
||||
} // closenessCentrality
|
||||
|
||||
}); // elesfn
|
||||
|
||||
// nice, short mathematical alias
|
||||
elesfn.cc = elesfn.closenessCentrality;
|
||||
elesfn.ccn = elesfn.closenessCentralityNormalised = elesfn.closenessCentralityNormalized;
|
||||
|
||||
export default elesfn;
|
||||
133
frontend/node_modules/cytoscape/src/collection/algorithms/dijkstra.mjs
generated
vendored
Normal file
133
frontend/node_modules/cytoscape/src/collection/algorithms/dijkstra.mjs
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
import * as is from '../../is.mjs';
|
||||
import Heap from '../../heap.mjs';
|
||||
import { defaults } from '../../util/index.mjs';
|
||||
|
||||
const dijkstraDefaults = defaults({
|
||||
root: null,
|
||||
weight: edge => 1,
|
||||
directed: false
|
||||
});
|
||||
|
||||
let elesfn = ({
|
||||
|
||||
dijkstra: function( options ){
|
||||
if( !is.plainObject(options) ){
|
||||
let args = arguments;
|
||||
|
||||
options = { root: args[0], weight: args[1], directed: args[2] };
|
||||
}
|
||||
|
||||
let { root, weight, directed } = dijkstraDefaults(options);
|
||||
|
||||
let eles = this;
|
||||
let weightFn = weight;
|
||||
let source = is.string( root ) ? this.filter( root )[0] : root[0];
|
||||
let dist = {};
|
||||
let prev = {};
|
||||
let knownDist = {};
|
||||
|
||||
let { nodes, edges } = this.byGroup();
|
||||
edges.unmergeBy( ele => ele.isLoop() );
|
||||
|
||||
let getDist = node => dist[ node.id() ];
|
||||
|
||||
let setDist = ( node, d ) => {
|
||||
dist[ node.id() ] = d;
|
||||
|
||||
Q.updateItem( node );
|
||||
};
|
||||
|
||||
let Q = new Heap( (a, b) => getDist(a) - getDist(b) );
|
||||
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let node = nodes[ i ];
|
||||
|
||||
dist[ node.id() ] = node.same( source ) ? 0 : Infinity;
|
||||
Q.push( node );
|
||||
}
|
||||
|
||||
let distBetween = ( u, v ) => {
|
||||
let uvs = ( directed ? u.edgesTo(v) : u.edgesWith(v) ).intersect( edges );
|
||||
let smallestDistance = Infinity;
|
||||
let smallestEdge;
|
||||
|
||||
for( let i = 0; i < uvs.length; i++ ){
|
||||
let edge = uvs[ i ];
|
||||
let weight = weightFn( edge );
|
||||
|
||||
if( weight < smallestDistance || !smallestEdge ){
|
||||
smallestDistance = weight;
|
||||
smallestEdge = edge;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
edge: smallestEdge,
|
||||
dist: smallestDistance
|
||||
};
|
||||
};
|
||||
|
||||
while( Q.size() > 0 ){
|
||||
let u = Q.pop();
|
||||
let smalletsDist = getDist( u );
|
||||
let uid = u.id();
|
||||
|
||||
knownDist[ uid ] = smalletsDist;
|
||||
|
||||
if( smalletsDist === Infinity ){
|
||||
continue;
|
||||
}
|
||||
|
||||
let neighbors = u.neighborhood().intersect( nodes );
|
||||
for( let i = 0; i < neighbors.length; i++ ){
|
||||
let v = neighbors[ i ];
|
||||
let vid = v.id();
|
||||
let vDist = distBetween( u, v );
|
||||
|
||||
let alt = smalletsDist + vDist.dist;
|
||||
|
||||
if( alt < getDist( v ) ){
|
||||
setDist( v, alt );
|
||||
|
||||
prev[ vid ] = {
|
||||
node: u,
|
||||
edge: vDist.edge
|
||||
};
|
||||
}
|
||||
} // for
|
||||
} // while
|
||||
|
||||
return {
|
||||
distanceTo: function( node ){
|
||||
let target = is.string( node ) ? nodes.filter( node )[0] : node[0];
|
||||
|
||||
return knownDist[ target.id() ];
|
||||
},
|
||||
|
||||
pathTo: function( node ){
|
||||
let target = is.string( node ) ? nodes.filter( node )[0] : node[0];
|
||||
let S = [];
|
||||
let u = target;
|
||||
let uid = u.id();
|
||||
|
||||
if( target.length > 0 ){
|
||||
S.unshift( target );
|
||||
|
||||
while( prev[ uid ] ){
|
||||
let p = prev[ uid ];
|
||||
|
||||
S.unshift( p.edge );
|
||||
S.unshift( p.node );
|
||||
|
||||
u = p.node;
|
||||
uid = u.id();
|
||||
}
|
||||
}
|
||||
|
||||
return eles.spawn( S );
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export default elesfn;
|
||||
118
frontend/node_modules/cytoscape/src/collection/algorithms/hopcroft-tarjan-biconnected.mjs
generated
vendored
Normal file
118
frontend/node_modules/cytoscape/src/collection/algorithms/hopcroft-tarjan-biconnected.mjs
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
let hopcroftTarjanBiconnected = function() {
|
||||
let eles = this;
|
||||
let nodes = {};
|
||||
let id = 0;
|
||||
let edgeCount = 0;
|
||||
let components = [];
|
||||
let stack = [];
|
||||
let visitedEdges = {};
|
||||
|
||||
const buildComponent = (x, y) => {
|
||||
let i = stack.length-1;
|
||||
let cutset = [];
|
||||
let component = eles.spawn();
|
||||
|
||||
while (stack[i].x != x || stack[i].y != y) {
|
||||
cutset.push(stack.pop().edge);
|
||||
i--;
|
||||
}
|
||||
cutset.push(stack.pop().edge);
|
||||
|
||||
cutset.forEach(edge => {
|
||||
let connectedNodes = edge.connectedNodes()
|
||||
.intersection(eles);
|
||||
component.merge(edge);
|
||||
connectedNodes.forEach(node => {
|
||||
const nodeId = node.id();
|
||||
const connectedEdges = node.connectedEdges()
|
||||
.intersection(eles);
|
||||
component.merge(node);
|
||||
if (!nodes[nodeId].cutVertex) {
|
||||
component.merge(connectedEdges);
|
||||
} else {
|
||||
component.merge(connectedEdges.filter(edge => edge.isLoop()));
|
||||
}
|
||||
});
|
||||
});
|
||||
components.push(component);
|
||||
};
|
||||
|
||||
const biconnectedSearch = (root, currentNode, parent) => {
|
||||
if (root === parent) edgeCount += 1;
|
||||
nodes[currentNode] = {
|
||||
id : id,
|
||||
low : id++,
|
||||
cutVertex : false
|
||||
};
|
||||
let edges = eles.getElementById(currentNode)
|
||||
.connectedEdges()
|
||||
.intersection(eles);
|
||||
|
||||
if (edges.size() === 0) {
|
||||
components.push(eles.spawn(eles.getElementById(currentNode)));
|
||||
} else {
|
||||
let sourceId, targetId, otherNodeId, edgeId;
|
||||
|
||||
edges.forEach(edge => {
|
||||
sourceId = edge.source().id();
|
||||
targetId = edge.target().id();
|
||||
otherNodeId = (sourceId === currentNode) ? targetId : sourceId;
|
||||
|
||||
if (otherNodeId !== parent) {
|
||||
edgeId = edge.id();
|
||||
|
||||
if (!visitedEdges[edgeId]) {
|
||||
visitedEdges[edgeId] = true;
|
||||
stack.push({
|
||||
x : currentNode,
|
||||
y : otherNodeId,
|
||||
edge
|
||||
});
|
||||
}
|
||||
|
||||
if (!(otherNodeId in nodes)) {
|
||||
biconnectedSearch(root, otherNodeId, currentNode);
|
||||
nodes[currentNode].low = Math.min(nodes[currentNode].low,
|
||||
nodes[otherNodeId].low);
|
||||
|
||||
if (nodes[currentNode].id <= nodes[otherNodeId].low) {
|
||||
nodes[currentNode].cutVertex = true;
|
||||
buildComponent(currentNode, otherNodeId);
|
||||
}
|
||||
} else {
|
||||
nodes[currentNode].low = Math.min(nodes[currentNode].low,
|
||||
nodes[otherNodeId].id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
eles.forEach(ele => {
|
||||
if (ele.isNode()) {
|
||||
let nodeId = ele.id();
|
||||
|
||||
if (!(nodeId in nodes)) {
|
||||
edgeCount = 0;
|
||||
biconnectedSearch(nodeId, nodeId);
|
||||
nodes[nodeId].cutVertex = (edgeCount > 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let cutVertices = Object.keys(nodes)
|
||||
.filter(id => nodes[id].cutVertex)
|
||||
.map(id => eles.getElementById(id));
|
||||
|
||||
return {
|
||||
cut: eles.spawn(cutVertices),
|
||||
components
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
hopcroftTarjanBiconnected,
|
||||
htbc: hopcroftTarjanBiconnected,
|
||||
htb: hopcroftTarjanBiconnected,
|
||||
hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
|
||||
};
|
||||
34
frontend/node_modules/cytoscape/src/collection/cache-traversal-call.mjs
generated
vendored
Normal file
34
frontend/node_modules/cytoscape/src/collection/cache-traversal-call.mjs
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
import * as is from '../is.mjs';
|
||||
import * as util from '../util/index.mjs';
|
||||
|
||||
let cache = function( fn, name ){
|
||||
return function traversalCache( arg1, arg2, arg3, arg4 ){
|
||||
let selectorOrEles = arg1;
|
||||
let eles = this;
|
||||
let key;
|
||||
|
||||
if( selectorOrEles == null ){
|
||||
key = '';
|
||||
} else if( is.elementOrCollection( selectorOrEles ) && selectorOrEles.length === 1 ){
|
||||
key = selectorOrEles.id();
|
||||
}
|
||||
|
||||
if( eles.length === 1 && key ){
|
||||
let _p = eles[0]._private;
|
||||
let tch = _p.traversalCache = _p.traversalCache || {};
|
||||
let ch = tch[ name ] = tch[ name ] || [];
|
||||
let hash = util.hashString( key );
|
||||
let cacheHit = ch[ hash ];
|
||||
|
||||
if( cacheHit ){
|
||||
return cacheHit;
|
||||
} else {
|
||||
return ( ch[ hash ] = fn.call( eles, arg1, arg2, arg3, arg4 ) );
|
||||
}
|
||||
} else {
|
||||
return fn.call( eles, arg1, arg2, arg3, arg4 );
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default cache;
|
||||
86
frontend/node_modules/cytoscape/src/collection/data.mjs
generated
vendored
Normal file
86
frontend/node_modules/cytoscape/src/collection/data.mjs
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
import define from '../define/index.mjs';
|
||||
|
||||
let fn, elesfn;
|
||||
|
||||
fn = elesfn = ({
|
||||
|
||||
data: define.data( {
|
||||
field: 'data',
|
||||
bindingEvent: 'data',
|
||||
allowBinding: true,
|
||||
allowSetting: true,
|
||||
settingEvent: 'data',
|
||||
settingTriggersEvent: true,
|
||||
triggerFnName: 'trigger',
|
||||
allowGetting: true,
|
||||
immutableKeys: {
|
||||
'id': true,
|
||||
'source': true,
|
||||
'target': true,
|
||||
'parent': true
|
||||
},
|
||||
updateStyle: true
|
||||
} ),
|
||||
|
||||
removeData: define.removeData( {
|
||||
field: 'data',
|
||||
event: 'data',
|
||||
triggerFnName: 'trigger',
|
||||
triggerEvent: true,
|
||||
immutableKeys: {
|
||||
'id': true,
|
||||
'source': true,
|
||||
'target': true,
|
||||
'parent': true
|
||||
},
|
||||
updateStyle: true
|
||||
} ),
|
||||
|
||||
scratch: define.data( {
|
||||
field: 'scratch',
|
||||
bindingEvent: 'scratch',
|
||||
allowBinding: true,
|
||||
allowSetting: true,
|
||||
settingEvent: 'scratch',
|
||||
settingTriggersEvent: true,
|
||||
triggerFnName: 'trigger',
|
||||
allowGetting: true,
|
||||
updateStyle: true
|
||||
} ),
|
||||
|
||||
removeScratch: define.removeData( {
|
||||
field: 'scratch',
|
||||
event: 'scratch',
|
||||
triggerFnName: 'trigger',
|
||||
triggerEvent: true,
|
||||
updateStyle: true
|
||||
} ),
|
||||
|
||||
rscratch: define.data( {
|
||||
field: 'rscratch',
|
||||
allowBinding: false,
|
||||
allowSetting: true,
|
||||
settingTriggersEvent: false,
|
||||
allowGetting: true
|
||||
} ),
|
||||
|
||||
removeRscratch: define.removeData( {
|
||||
field: 'rscratch',
|
||||
triggerEvent: false
|
||||
} ),
|
||||
|
||||
id: function(){
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return ele._private.data.id;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// aliases
|
||||
fn.attr = fn.data;
|
||||
fn.removeAttr = fn.removeData;
|
||||
|
||||
export default elesfn;
|
||||
56
frontend/node_modules/cytoscape/src/collection/dimensions/edge-points.mjs
generated
vendored
Normal file
56
frontend/node_modules/cytoscape/src/collection/dimensions/edge-points.mjs
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
import * as math from '../../math.mjs';
|
||||
|
||||
const ifEdge = (ele, getValue) => {
|
||||
if( ele.isEdge() && ele.takesUpSpace() ){
|
||||
return getValue( ele );
|
||||
}
|
||||
};
|
||||
|
||||
const ifEdgeRenderedPosition = (ele, getPoint) => {
|
||||
if( ele.isEdge() && ele.takesUpSpace() ){
|
||||
let cy = ele.cy();
|
||||
|
||||
return math.modelToRenderedPosition( getPoint( ele ), cy.zoom(), cy.pan() );
|
||||
}
|
||||
};
|
||||
|
||||
const ifEdgeRenderedPositions = (ele, getPoints) => {
|
||||
if( ele.isEdge() && ele.takesUpSpace() ){
|
||||
let cy = ele.cy();
|
||||
let pan = cy.pan();
|
||||
let zoom = cy.zoom();
|
||||
|
||||
return getPoints( ele ).map( p => math.modelToRenderedPosition( p, zoom, pan ) );
|
||||
}
|
||||
};
|
||||
|
||||
const controlPoints = ele => ele.renderer().getControlPoints( ele );
|
||||
const segmentPoints = ele => ele.renderer().getSegmentPoints( ele );
|
||||
const sourceEndpoint = ele => ele.renderer().getSourceEndpoint( ele );
|
||||
const targetEndpoint = ele => ele.renderer().getTargetEndpoint( ele );
|
||||
const midpoint = ele => ele.renderer().getEdgeMidpoint( ele );
|
||||
|
||||
const pts = {
|
||||
controlPoints: { get: controlPoints, mult: true },
|
||||
segmentPoints: { get: segmentPoints, mult: true },
|
||||
sourceEndpoint: { get: sourceEndpoint },
|
||||
targetEndpoint: { get: targetEndpoint },
|
||||
midpoint: { get: midpoint }
|
||||
};
|
||||
|
||||
const renderedName = name => 'rendered' + name[0].toUpperCase() + name.substr(1);
|
||||
|
||||
export default Object.keys( pts ).reduce( ( obj, name ) => {
|
||||
let spec = pts[ name ];
|
||||
let rName = renderedName( name );
|
||||
|
||||
obj[ name ] = function(){ return ifEdge( this, spec.get ); };
|
||||
|
||||
if( spec.mult ){
|
||||
obj[ rName ] = function(){ return ifEdgeRenderedPositions( this, spec.get ); };
|
||||
} else {
|
||||
obj[ rName ] = function(){ return ifEdgeRenderedPosition( this, spec.get ); };
|
||||
}
|
||||
|
||||
return obj;
|
||||
}, {} );
|
||||
130
frontend/node_modules/cytoscape/src/collection/element.mjs
generated
vendored
Normal file
130
frontend/node_modules/cytoscape/src/collection/element.mjs
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
import * as util from '../util/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import Set from '../set.mjs';
|
||||
|
||||
// represents a node or an edge
|
||||
let Element = function( cy, params, restore = true ){
|
||||
if( cy === undefined || params === undefined || !is.core( cy ) ){
|
||||
util.error( 'An element must have a core reference and parameters set' );
|
||||
return;
|
||||
}
|
||||
|
||||
let group = params.group;
|
||||
|
||||
// try to automatically infer the group if unspecified
|
||||
if( group == null ){
|
||||
if( params.data && params.data.source != null && params.data.target != null ){
|
||||
group = 'edges';
|
||||
} else {
|
||||
group = 'nodes';
|
||||
}
|
||||
}
|
||||
|
||||
// validate group
|
||||
if( group !== 'nodes' && group !== 'edges' ){
|
||||
util.error( 'An element must be of type `nodes` or `edges`; you specified `' + group + '`' );
|
||||
return;
|
||||
}
|
||||
|
||||
// make the element array-like, just like a collection
|
||||
this.length = 1;
|
||||
this[0] = this;
|
||||
|
||||
// NOTE: when something is added here, add also to ele.json()
|
||||
let _p = this._private = {
|
||||
cy: cy,
|
||||
single: true, // indicates this is an element
|
||||
data: params.data || {}, // data object
|
||||
position: params.position || { x: 0, y: 0 }, // (x, y) position pair
|
||||
autoWidth: undefined, // width and height of nodes calculated by the renderer when set to special 'auto' value
|
||||
autoHeight: undefined,
|
||||
autoPadding: undefined,
|
||||
compoundBoundsClean: false, // whether the compound dimensions need to be recalculated the next time dimensions are read
|
||||
listeners: [], // array of bound listeners
|
||||
group: group, // string; 'nodes' or 'edges'
|
||||
style: {}, // properties as set by the style
|
||||
rstyle: {}, // properties for style sent from the renderer to the core
|
||||
styleCxts: [], // applied style contexts from the styler
|
||||
styleKeys: {}, // per-group keys of style property values
|
||||
removed: true, // whether it's inside the vis; true if removed (set true here since we call restore)
|
||||
selected: params.selected ? true : false, // whether it's selected
|
||||
selectable: params.selectable === undefined ? true : ( params.selectable ? true : false ), // whether it's selectable
|
||||
locked: params.locked ? true : false, // whether the element is locked (cannot be moved)
|
||||
grabbed: false, // whether the element is grabbed by the mouse; renderer sets this privately
|
||||
grabbable: params.grabbable === undefined ? true : ( params.grabbable ? true : false ), // whether the element can be grabbed
|
||||
pannable: params.pannable === undefined ? (group === 'edges' ? true : false) : ( params.pannable ? true : false ), // whether the element has passthrough panning enabled
|
||||
active: false, // whether the element is active from user interaction
|
||||
classes: new Set(), // map ( className => true )
|
||||
animation: { // object for currently-running animations
|
||||
current: [],
|
||||
queue: []
|
||||
},
|
||||
rscratch: {}, // object in which the renderer can store information
|
||||
scratch: params.scratch || {}, // scratch objects
|
||||
edges: [], // array of connected edges
|
||||
children: [], // array of children
|
||||
parent: params.parent && params.parent.isNode() ? params.parent : null, // parent ref
|
||||
traversalCache: {}, // cache of output of traversal functions
|
||||
backgrounding: false, // whether background images are loading
|
||||
bbCache: null, // cache of the current bounding box
|
||||
bbCacheShift: { x: 0, y: 0 }, // shift applied to cached bb to be applied on next get
|
||||
bodyBounds: null, // bounds cache of element body, w/o overlay
|
||||
overlayBounds: null, // bounds cache of element body, including overlay
|
||||
labelBounds: { // bounds cache of labels
|
||||
all: null,
|
||||
source: null,
|
||||
target: null,
|
||||
main: null
|
||||
},
|
||||
arrowBounds: { // bounds cache of edge arrows
|
||||
source: null,
|
||||
target: null,
|
||||
'mid-source': null,
|
||||
'mid-target': null
|
||||
}
|
||||
};
|
||||
|
||||
if( _p.position.x == null ){ _p.position.x = 0; }
|
||||
if( _p.position.y == null ){ _p.position.y = 0; }
|
||||
|
||||
// renderedPosition overrides if specified
|
||||
if( params.renderedPosition ){
|
||||
let rpos = params.renderedPosition;
|
||||
let pan = cy.pan();
|
||||
let zoom = cy.zoom();
|
||||
|
||||
_p.position = {
|
||||
x: (rpos.x - pan.x) / zoom,
|
||||
y: (rpos.y - pan.y) / zoom
|
||||
};
|
||||
}
|
||||
|
||||
let classes = [];
|
||||
if( is.array( params.classes ) ){
|
||||
classes = params.classes;
|
||||
} else if( is.string( params.classes ) ){
|
||||
classes = params.classes.split( /\s+/ );
|
||||
}
|
||||
for( let i = 0, l = classes.length; i < l; i++ ){
|
||||
let cls = classes[ i ];
|
||||
if( !cls || cls === '' ){ continue; }
|
||||
|
||||
_p.classes.add(cls);
|
||||
}
|
||||
|
||||
this.createEmitter();
|
||||
|
||||
if( restore === undefined || restore ){
|
||||
this.restore();
|
||||
}
|
||||
|
||||
let bypass = params.style || params.css;
|
||||
if( bypass ){
|
||||
util.warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
|
||||
|
||||
this.style(bypass);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default Element;
|
||||
196
frontend/node_modules/cytoscape/src/collection/layout.mjs
generated
vendored
Normal file
196
frontend/node_modules/cytoscape/src/collection/layout.mjs
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
import * as util from '../util/index.mjs';
|
||||
import Promise from '../promise.mjs';
|
||||
import * as math from '../math.mjs';
|
||||
|
||||
const getLayoutDimensionOptions = util.defaults({
|
||||
nodeDimensionsIncludeLabels: false
|
||||
});
|
||||
|
||||
let elesfn = ({
|
||||
// Calculates and returns node dimensions { x, y } based on options given
|
||||
layoutDimensions: function( options ){
|
||||
options = getLayoutDimensionOptions( options );
|
||||
|
||||
let dims;
|
||||
|
||||
if( !this.takesUpSpace() ){
|
||||
dims = { w: 0, h: 0 };
|
||||
} else if( options.nodeDimensionsIncludeLabels ){
|
||||
let bbDim = this.boundingBox();
|
||||
|
||||
dims = {
|
||||
w: bbDim.w,
|
||||
h: bbDim.h
|
||||
};
|
||||
} else {
|
||||
dims = {
|
||||
w: this.outerWidth(),
|
||||
h: this.outerHeight()
|
||||
};
|
||||
}
|
||||
|
||||
// sanitise the dimensions for external layouts (avoid division by zero)
|
||||
if( dims.w === 0 || dims.h === 0 ){
|
||||
dims.w = dims.h = 1;
|
||||
}
|
||||
|
||||
return dims;
|
||||
},
|
||||
|
||||
// using standard layout options, apply position function (w/ or w/o animation)
|
||||
layoutPositions: function( layout, options, fn ){
|
||||
let nodes = this.nodes().filter(n => !n.isParent());
|
||||
let cy = this.cy();
|
||||
let layoutEles = options.eles; // nodes & edges
|
||||
let getMemoizeKey = node => node.id();
|
||||
let fnMem = util.memoize( fn, getMemoizeKey ); // memoized version of position function
|
||||
|
||||
layout.emit( { type: 'layoutstart', layout: layout } );
|
||||
|
||||
layout.animations = [];
|
||||
|
||||
let calculateSpacing = function( spacing, nodesBb, pos ){
|
||||
let center = {
|
||||
x: nodesBb.x1 + nodesBb.w / 2,
|
||||
y: nodesBb.y1 + nodesBb.h / 2
|
||||
};
|
||||
|
||||
let spacingVector = { // scale from center of bounding box (not necessarily 0,0)
|
||||
x: (pos.x - center.x) * spacing,
|
||||
y: (pos.y - center.y) * spacing
|
||||
};
|
||||
|
||||
return {
|
||||
x: center.x + spacingVector.x,
|
||||
y: center.y + spacingVector.y
|
||||
};
|
||||
};
|
||||
|
||||
let useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
|
||||
|
||||
let spacingBb = function(){
|
||||
if( !useSpacingFactor ){ return null; }
|
||||
|
||||
let bb = math.makeBoundingBox();
|
||||
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let node = nodes[i];
|
||||
let pos = fnMem( node, i );
|
||||
|
||||
math.expandBoundingBoxByPoint( bb, pos.x, pos.y );
|
||||
}
|
||||
|
||||
return bb;
|
||||
};
|
||||
|
||||
let bb = spacingBb();
|
||||
|
||||
let getFinalPos = util.memoize( function( node, i ){
|
||||
let newPos = fnMem( node, i );
|
||||
|
||||
if( useSpacingFactor ){
|
||||
let spacing = Math.abs( options.spacingFactor );
|
||||
|
||||
newPos = calculateSpacing( spacing, bb, newPos );
|
||||
}
|
||||
|
||||
if( options.transform != null ){
|
||||
newPos = options.transform( node, newPos );
|
||||
}
|
||||
|
||||
return newPos;
|
||||
}, getMemoizeKey );
|
||||
|
||||
if( options.animate ){
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let node = nodes[ i ];
|
||||
let newPos = getFinalPos( node, i );
|
||||
let animateNode = options.animateFilter == null || options.animateFilter( node, i );
|
||||
|
||||
if( animateNode ){
|
||||
let ani = node.animation( {
|
||||
position: newPos,
|
||||
duration: options.animationDuration,
|
||||
easing: options.animationEasing
|
||||
} );
|
||||
|
||||
layout.animations.push( ani );
|
||||
} else {
|
||||
node.position( newPos );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if( options.fit ){
|
||||
let fitAni = cy.animation({
|
||||
fit: {
|
||||
boundingBox: layoutEles.boundingBoxAt( getFinalPos ),
|
||||
padding: options.padding
|
||||
},
|
||||
duration: options.animationDuration,
|
||||
easing: options.animationEasing
|
||||
});
|
||||
|
||||
layout.animations.push( fitAni );
|
||||
} else if( options.zoom !== undefined && options.pan !== undefined ){
|
||||
let zoomPanAni = cy.animation({
|
||||
zoom: options.zoom,
|
||||
pan: options.pan,
|
||||
duration: options.animationDuration,
|
||||
easing: options.animationEasing
|
||||
});
|
||||
|
||||
layout.animations.push( zoomPanAni );
|
||||
}
|
||||
|
||||
layout.animations.forEach(ani => ani.play());
|
||||
|
||||
layout.one( 'layoutready', options.ready );
|
||||
layout.emit( { type: 'layoutready', layout: layout } );
|
||||
|
||||
Promise.all( layout.animations.map(function( ani ){
|
||||
return ani.promise();
|
||||
}) ).then(function(){
|
||||
layout.one( 'layoutstop', options.stop );
|
||||
layout.emit( { type: 'layoutstop', layout: layout } );
|
||||
});
|
||||
} else {
|
||||
|
||||
nodes.positions( getFinalPos );
|
||||
|
||||
if( options.fit ){
|
||||
cy.fit( options.eles, options.padding );
|
||||
}
|
||||
|
||||
if( options.zoom != null ){
|
||||
cy.zoom( options.zoom );
|
||||
}
|
||||
|
||||
if( options.pan ){
|
||||
cy.pan( options.pan );
|
||||
}
|
||||
|
||||
layout.one( 'layoutready', options.ready );
|
||||
layout.emit( { type: 'layoutready', layout: layout } );
|
||||
|
||||
layout.one( 'layoutstop', options.stop );
|
||||
layout.emit( { type: 'layoutstop', layout: layout } );
|
||||
}
|
||||
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
layout: function( options ){
|
||||
let cy = this.cy();
|
||||
|
||||
return cy.makeLayout( util.extend( {}, options, {
|
||||
eles: this
|
||||
} ) );
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// aliases:
|
||||
elesfn.createLayout = elesfn.makeLayout = elesfn.layout;
|
||||
|
||||
export default elesfn;
|
||||
457
frontend/node_modules/cytoscape/src/collection/style.mjs
generated
vendored
Normal file
457
frontend/node_modules/cytoscape/src/collection/style.mjs
generated
vendored
Normal file
@@ -0,0 +1,457 @@
|
||||
import * as is from '../is.mjs';
|
||||
import * as util from '../util/index.mjs';
|
||||
|
||||
function styleCache( key, fn, ele ){
|
||||
var _p = ele._private;
|
||||
var cache = _p.styleCache = _p.styleCache || [];
|
||||
var val;
|
||||
|
||||
if( (val = cache[key]) != null ){
|
||||
return val;
|
||||
} else {
|
||||
val = cache[key] = fn( ele );
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
function cacheStyleFunction( key, fn ){
|
||||
key = util.hashString( key );
|
||||
|
||||
return function cachedStyleFunction( ele ){
|
||||
return styleCache( key, fn, ele );
|
||||
};
|
||||
}
|
||||
|
||||
function cachePrototypeStyleFunction( key, fn ){
|
||||
key = util.hashString( key );
|
||||
|
||||
let selfFn = ele => fn.call( ele );
|
||||
|
||||
return function cachedPrototypeStyleFunction(){
|
||||
var ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return styleCache( key, selfFn, ele );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let elesfn = ({
|
||||
|
||||
recalculateRenderedStyle: function( useCache ){
|
||||
let cy = this.cy();
|
||||
let renderer = cy.renderer();
|
||||
let styleEnabled = cy.styleEnabled();
|
||||
|
||||
if( renderer && styleEnabled ){
|
||||
renderer.recalculateRenderedStyle( this, useCache );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
dirtyStyleCache: function(){
|
||||
let cy = this.cy();
|
||||
let dirty = ele => ele._private.styleCache = null;
|
||||
|
||||
if( cy.hasCompoundNodes() ){
|
||||
let eles;
|
||||
|
||||
eles = this.spawnSelf()
|
||||
.merge( this.descendants() )
|
||||
.merge( this.parents() )
|
||||
;
|
||||
|
||||
eles.merge( eles.connectedEdges() );
|
||||
|
||||
eles.forEach( dirty );
|
||||
} else {
|
||||
this.forEach( ele => {
|
||||
dirty( ele );
|
||||
|
||||
ele.connectedEdges().forEach( dirty );
|
||||
} );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// fully updates (recalculates) the style for the elements
|
||||
updateStyle: function( notifyRenderer ){
|
||||
let cy = this._private.cy;
|
||||
|
||||
if( !cy.styleEnabled() ){ return this; }
|
||||
|
||||
if( cy.batching() ){
|
||||
let bEles = cy._private.batchStyleEles;
|
||||
|
||||
bEles.merge( this );
|
||||
|
||||
return this; // chaining and exit early when batching
|
||||
}
|
||||
|
||||
let hasCompounds = cy.hasCompoundNodes();
|
||||
let updatedEles = this;
|
||||
|
||||
notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
|
||||
|
||||
if( hasCompounds ){ // then add everything up and down for compound selector checks
|
||||
updatedEles = this.spawnSelf().merge( this.descendants() ).merge( this.parents() );
|
||||
}
|
||||
|
||||
// let changedEles = style.apply( updatedEles );
|
||||
let changedEles = updatedEles;
|
||||
|
||||
if( notifyRenderer ){
|
||||
changedEles.emitAndNotify( 'style' ); // let renderer know we changed style
|
||||
} else {
|
||||
changedEles.emit( 'style' ); // just fire the event
|
||||
}
|
||||
|
||||
updatedEles.forEach(ele => ele._private.styleDirty = true);
|
||||
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
// private: clears dirty flag and recalculates style
|
||||
cleanStyle: function(){
|
||||
let cy = this.cy();
|
||||
|
||||
if( !cy.styleEnabled() ){ return; }
|
||||
|
||||
for( let i = 0; i < this.length; i++ ){
|
||||
let ele = this[i];
|
||||
|
||||
if( ele._private.styleDirty ){
|
||||
// n.b. this flag should be set before apply() to avoid potential infinite recursion
|
||||
ele._private.styleDirty = false;
|
||||
|
||||
cy.style().apply(ele);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// get the internal parsed style object for the specified property
|
||||
parsedStyle: function( property, includeNonDefault = true ){
|
||||
let ele = this[0];
|
||||
let cy = ele.cy();
|
||||
|
||||
if( !cy.styleEnabled() ){ return; }
|
||||
|
||||
if( ele ){
|
||||
// this.cleanStyle();
|
||||
|
||||
// Inline the important part of cleanStyle(), for raw performance
|
||||
if( ele._private.styleDirty ){
|
||||
// n.b. this flag should be set before apply() to avoid potential infinite recursion
|
||||
ele._private.styleDirty = false;
|
||||
cy.style().apply(ele);
|
||||
}
|
||||
|
||||
let overriddenStyle = ele._private.style[ property ];
|
||||
|
||||
if( overriddenStyle != null ){
|
||||
return overriddenStyle;
|
||||
} else if( includeNonDefault ){
|
||||
return cy.style().getDefaultProperty( property );
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
numericStyle: function( property ){
|
||||
let ele = this[0];
|
||||
|
||||
if( !ele.cy().styleEnabled() ){ return; }
|
||||
|
||||
if( ele ){
|
||||
let pstyle = ele.pstyle( property );
|
||||
|
||||
return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
|
||||
}
|
||||
},
|
||||
|
||||
numericStyleUnits: function( property ){
|
||||
let ele = this[0];
|
||||
|
||||
if( !ele.cy().styleEnabled() ){ return; }
|
||||
|
||||
if( ele ){
|
||||
return ele.pstyle( property ).units;
|
||||
}
|
||||
},
|
||||
|
||||
// get the specified css property as a rendered value (i.e. on-screen value)
|
||||
// or get the whole rendered style if no property specified (NB doesn't allow setting)
|
||||
renderedStyle: function( property ){
|
||||
let cy = this.cy();
|
||||
if( !cy.styleEnabled() ){ return this; }
|
||||
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return cy.style().getRenderedStyle( ele, property );
|
||||
}
|
||||
},
|
||||
|
||||
// read the calculated css style of the element or override the style (via a bypass)
|
||||
style: function( name, value ){
|
||||
let cy = this.cy();
|
||||
|
||||
if( !cy.styleEnabled() ){ return this; }
|
||||
|
||||
let updateTransitions = false;
|
||||
let style = cy.style();
|
||||
|
||||
if( is.plainObject( name ) ){ // then extend the bypass
|
||||
let props = name;
|
||||
style.applyBypass( this, props, updateTransitions );
|
||||
|
||||
this.emitAndNotify( 'style' ); // let the renderer know we've updated style
|
||||
|
||||
} else if( is.string( name ) ){
|
||||
|
||||
if( value === undefined ){ // then get the property from the style
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return style.getStylePropertyValue( ele, name );
|
||||
} else { // empty collection => can't get any value
|
||||
return;
|
||||
}
|
||||
|
||||
} else { // then set the bypass with the property value
|
||||
style.applyBypass( this, name, value, updateTransitions );
|
||||
|
||||
this.emitAndNotify( 'style' ); // let the renderer know we've updated style
|
||||
}
|
||||
|
||||
} else if( name === undefined ){
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return style.getRawStyle( ele );
|
||||
} else { // empty collection => can't get any value
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
removeStyle: function( names ){
|
||||
let cy = this.cy();
|
||||
|
||||
if( !cy.styleEnabled() ){ return this; }
|
||||
|
||||
let updateTransitions = false;
|
||||
let style = cy.style();
|
||||
let eles = this;
|
||||
|
||||
if( names === undefined ){
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
|
||||
style.removeAllBypasses( ele, updateTransitions );
|
||||
}
|
||||
} else {
|
||||
names = names.split( /\s+/ );
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
|
||||
style.removeBypasses( ele, names, updateTransitions );
|
||||
}
|
||||
}
|
||||
|
||||
this.emitAndNotify( 'style' ); // let the renderer know we've updated style
|
||||
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
show: function(){
|
||||
this.css( 'display', 'element' );
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
hide: function(){
|
||||
this.css( 'display', 'none' );
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
effectiveOpacity: function(){
|
||||
let cy = this.cy();
|
||||
if( !cy.styleEnabled() ){ return 1; }
|
||||
|
||||
let hasCompoundNodes = cy.hasCompoundNodes();
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
let _p = ele._private;
|
||||
let parentOpacity = ele.pstyle( 'opacity' ).value;
|
||||
|
||||
if( !hasCompoundNodes ){ return parentOpacity; }
|
||||
|
||||
let parents = !_p.data.parent ? null : ele.parents();
|
||||
|
||||
if( parents ){
|
||||
for( let i = 0; i < parents.length; i++ ){
|
||||
let parent = parents[ i ];
|
||||
let opacity = parent.pstyle( 'opacity' ).value;
|
||||
|
||||
parentOpacity = opacity * parentOpacity;
|
||||
}
|
||||
}
|
||||
|
||||
return parentOpacity;
|
||||
}
|
||||
},
|
||||
|
||||
transparent: function(){
|
||||
let cy = this.cy();
|
||||
if( !cy.styleEnabled() ){ return false; }
|
||||
|
||||
let ele = this[0];
|
||||
let hasCompoundNodes = ele.cy().hasCompoundNodes();
|
||||
|
||||
if( ele ){
|
||||
if( !hasCompoundNodes ){
|
||||
return ele.pstyle( 'opacity' ).value === 0;
|
||||
} else {
|
||||
return ele.effectiveOpacity() === 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
backgrounding: function(){
|
||||
let cy = this.cy();
|
||||
if( !cy.styleEnabled() ){ return false; }
|
||||
|
||||
let ele = this[0];
|
||||
|
||||
return ele._private.backgrounding ? true : false;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function checkCompound( ele, parentOk ){
|
||||
let _p = ele._private;
|
||||
let parents = _p.data.parent ? ele.parents() : null;
|
||||
|
||||
if( parents ){ for( let i = 0; i < parents.length; i++ ){
|
||||
let parent = parents[ i ];
|
||||
|
||||
if( !parentOk( parent ) ){ return false; }
|
||||
} }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function defineDerivedStateFunction( specs ){
|
||||
let ok = specs.ok;
|
||||
let edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
|
||||
let parentOk = specs.parentOk || specs.ok;
|
||||
|
||||
return function(){
|
||||
let cy = this.cy();
|
||||
if( !cy.styleEnabled() ){ return true; }
|
||||
|
||||
let ele = this[0];
|
||||
let hasCompoundNodes = cy.hasCompoundNodes();
|
||||
|
||||
if( ele ){
|
||||
let _p = ele._private;
|
||||
|
||||
if( !ok( ele ) ){ return false; }
|
||||
|
||||
if( ele.isNode() ){
|
||||
return !hasCompoundNodes || checkCompound( ele, parentOk );
|
||||
} else {
|
||||
let src = _p.source;
|
||||
let tgt = _p.target;
|
||||
|
||||
return ( edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) ) &&
|
||||
( src === tgt || ( edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)) ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let eleTakesUpSpace = cacheStyleFunction( 'eleTakesUpSpace', function( ele ){
|
||||
return (
|
||||
ele.pstyle( 'display' ).value === 'element'
|
||||
&& ele.width() !== 0
|
||||
&& ( ele.isNode() ? ele.height() !== 0 : true )
|
||||
);
|
||||
} );
|
||||
|
||||
elesfn.takesUpSpace = cachePrototypeStyleFunction( 'takesUpSpace', defineDerivedStateFunction({
|
||||
ok: eleTakesUpSpace
|
||||
}) );
|
||||
|
||||
let eleInteractive = cacheStyleFunction( 'eleInteractive', function( ele ){
|
||||
return (
|
||||
ele.pstyle('events').value === 'yes'
|
||||
&& ele.pstyle('visibility').value === 'visible'
|
||||
&& eleTakesUpSpace( ele )
|
||||
);
|
||||
} );
|
||||
|
||||
let parentInteractive = cacheStyleFunction( 'parentInteractive', function( parent ){
|
||||
return (
|
||||
parent.pstyle('visibility').value === 'visible'
|
||||
&& eleTakesUpSpace( parent )
|
||||
);
|
||||
} );
|
||||
|
||||
elesfn.interactive = cachePrototypeStyleFunction( 'interactive', defineDerivedStateFunction({
|
||||
ok: eleInteractive,
|
||||
parentOk: parentInteractive,
|
||||
edgeOkViaNode: eleTakesUpSpace
|
||||
}) );
|
||||
|
||||
elesfn.noninteractive = function(){
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return !ele.interactive();
|
||||
}
|
||||
};
|
||||
|
||||
let eleVisible = cacheStyleFunction( 'eleVisible', function( ele ){
|
||||
return (
|
||||
ele.pstyle( 'visibility' ).value === 'visible'
|
||||
&& ele.pstyle( 'opacity' ).pfValue !== 0
|
||||
&& eleTakesUpSpace( ele )
|
||||
);
|
||||
} );
|
||||
|
||||
let edgeVisibleViaNode = eleTakesUpSpace;
|
||||
|
||||
elesfn.visible = cachePrototypeStyleFunction( 'visible', defineDerivedStateFunction({
|
||||
ok: eleVisible,
|
||||
edgeOkViaNode: edgeVisibleViaNode
|
||||
}) );
|
||||
|
||||
elesfn.hidden = function(){
|
||||
let ele = this[0];
|
||||
|
||||
if( ele ){
|
||||
return !ele.visible();
|
||||
}
|
||||
};
|
||||
|
||||
elesfn.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function(){
|
||||
if( !this.cy().styleEnabled() ){ return false; }
|
||||
|
||||
return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
|
||||
});
|
||||
|
||||
elesfn.bypass = elesfn.css = elesfn.style;
|
||||
elesfn.renderedCss = elesfn.renderedStyle;
|
||||
elesfn.removeBypass = elesfn.removeCss = elesfn.removeStyle;
|
||||
elesfn.pstyle = elesfn.parsedStyle;
|
||||
|
||||
export default elesfn;
|
||||
82
frontend/node_modules/cytoscape/src/core/add-remove.mjs
generated
vendored
Normal file
82
frontend/node_modules/cytoscape/src/core/add-remove.mjs
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
import * as is from '../is.mjs';
|
||||
import * as util from '../util/index.mjs';
|
||||
import Collection from '../collection/index.mjs';
|
||||
import Element from '../collection/element.mjs';
|
||||
|
||||
let corefn = {
|
||||
add: function( opts ){
|
||||
|
||||
let elements;
|
||||
let cy = this;
|
||||
|
||||
// add the elements
|
||||
if( is.elementOrCollection( opts ) ){
|
||||
let eles = opts;
|
||||
|
||||
if( eles._private.cy === cy ){ // same instance => just restore
|
||||
elements = eles.restore();
|
||||
|
||||
} else { // otherwise, copy from json
|
||||
let jsons = [];
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
jsons.push( ele.json() );
|
||||
}
|
||||
|
||||
elements = new Collection( cy, jsons );
|
||||
}
|
||||
}
|
||||
|
||||
// specify an array of options
|
||||
else if( is.array( opts ) ){
|
||||
let jsons = opts;
|
||||
|
||||
elements = new Collection( cy, jsons );
|
||||
}
|
||||
|
||||
// specify via opts.nodes and opts.edges
|
||||
else if( is.plainObject( opts ) && (is.array( opts.nodes ) || is.array( opts.edges )) ){
|
||||
let elesByGroup = opts;
|
||||
let jsons = [];
|
||||
|
||||
let grs = [ 'nodes', 'edges' ];
|
||||
for( let i = 0, il = grs.length; i < il; i++ ){
|
||||
let group = grs[ i ];
|
||||
let elesArray = elesByGroup[ group ];
|
||||
|
||||
if( is.array( elesArray ) ){
|
||||
|
||||
for( let j = 0, jl = elesArray.length; j < jl; j++ ){
|
||||
let json = util.extend( { group: group }, elesArray[ j ] );
|
||||
|
||||
jsons.push( json );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elements = new Collection( cy, jsons );
|
||||
}
|
||||
|
||||
// specify options for one element
|
||||
else {
|
||||
let json = opts;
|
||||
elements = (new Element( cy, json )).collection();
|
||||
}
|
||||
|
||||
return elements;
|
||||
},
|
||||
|
||||
remove: function( collection ){
|
||||
if( is.elementOrCollection( collection ) ){
|
||||
// already have right ref
|
||||
} else if( is.string( collection ) ){
|
||||
let selector = collection;
|
||||
collection = this.$( selector );
|
||||
}
|
||||
|
||||
return collection.remove();
|
||||
}
|
||||
};
|
||||
|
||||
export default corefn;
|
||||
82
frontend/node_modules/cytoscape/src/core/animation/ease.mjs
generated
vendored
Normal file
82
frontend/node_modules/cytoscape/src/core/animation/ease.mjs
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
import * as is from '../../is.mjs';
|
||||
|
||||
function getEasedValue( type, start, end, percent, easingFn ){
|
||||
if( percent === 1 ){
|
||||
return end;
|
||||
}
|
||||
|
||||
if( start === end ){
|
||||
return end;
|
||||
}
|
||||
|
||||
let val = easingFn( start, end, percent );
|
||||
|
||||
if( type == null ){
|
||||
return val;
|
||||
}
|
||||
|
||||
if( type.roundValue || type.color ){
|
||||
val = Math.round( val );
|
||||
}
|
||||
|
||||
if( type.min !== undefined ){
|
||||
val = Math.max( val, type.min );
|
||||
}
|
||||
|
||||
if( type.max !== undefined ){
|
||||
val = Math.min( val, type.max );
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
function getValue( prop, spec ){
|
||||
if( prop.pfValue != null || prop.value != null ){
|
||||
if( prop.pfValue != null && (spec == null || spec.type.units !== '%') ){
|
||||
return prop.pfValue;
|
||||
} else {
|
||||
return prop.value;
|
||||
}
|
||||
} else {
|
||||
return prop;
|
||||
}
|
||||
}
|
||||
|
||||
function ease( startProp, endProp, percent, easingFn, propSpec ){
|
||||
let type = propSpec != null ? propSpec.type : null;
|
||||
|
||||
if( percent < 0 ){
|
||||
percent = 0;
|
||||
} else if( percent > 1 ){
|
||||
percent = 1;
|
||||
}
|
||||
|
||||
let start = getValue( startProp, propSpec );
|
||||
let end = getValue( endProp, propSpec );
|
||||
|
||||
if( is.number( start ) && is.number( end ) ){
|
||||
return getEasedValue( type, start, end, percent, easingFn );
|
||||
|
||||
} else if( is.array( start ) && is.array( end ) ){
|
||||
let easedArr = [];
|
||||
|
||||
for( let i = 0; i < end.length; i++ ){
|
||||
let si = start[ i ];
|
||||
let ei = end[ i ];
|
||||
|
||||
if( si != null && ei != null ){
|
||||
let val = getEasedValue( type, si, ei, percent, easingFn );
|
||||
|
||||
easedArr.push( val );
|
||||
} else {
|
||||
easedArr.push( ei );
|
||||
}
|
||||
}
|
||||
|
||||
return easedArr;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export default ease;
|
||||
76
frontend/node_modules/cytoscape/src/core/animation/easings.mjs
generated
vendored
Normal file
76
frontend/node_modules/cytoscape/src/core/animation/easings.mjs
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
import generateCubicBezier from './cubic-bezier.mjs';
|
||||
import generateSpringRK4 from './spring.mjs';
|
||||
|
||||
let cubicBezier = function( t1, p1, t2, p2 ){
|
||||
let bezier = generateCubicBezier( t1, p1, t2, p2 );
|
||||
|
||||
return function( start, end, percent ){
|
||||
return start + ( end - start ) * bezier( percent );
|
||||
};
|
||||
};
|
||||
|
||||
let easings = {
|
||||
'linear': function( start, end, percent ){
|
||||
return start + (end - start) * percent;
|
||||
},
|
||||
|
||||
// default easings
|
||||
'ease': cubicBezier( 0.25, 0.1, 0.25, 1 ),
|
||||
'ease-in': cubicBezier( 0.42, 0, 1, 1 ),
|
||||
'ease-out': cubicBezier( 0, 0, 0.58, 1 ),
|
||||
'ease-in-out': cubicBezier( 0.42, 0, 0.58, 1 ),
|
||||
|
||||
// sine
|
||||
'ease-in-sine': cubicBezier( 0.47, 0, 0.745, 0.715 ),
|
||||
'ease-out-sine': cubicBezier( 0.39, 0.575, 0.565, 1 ),
|
||||
'ease-in-out-sine': cubicBezier( 0.445, 0.05, 0.55, 0.95 ),
|
||||
|
||||
// quad
|
||||
'ease-in-quad': cubicBezier( 0.55, 0.085, 0.68, 0.53 ),
|
||||
'ease-out-quad': cubicBezier( 0.25, 0.46, 0.45, 0.94 ),
|
||||
'ease-in-out-quad': cubicBezier( 0.455, 0.03, 0.515, 0.955 ),
|
||||
|
||||
// cubic
|
||||
'ease-in-cubic': cubicBezier( 0.55, 0.055, 0.675, 0.19 ),
|
||||
'ease-out-cubic': cubicBezier( 0.215, 0.61, 0.355, 1 ),
|
||||
'ease-in-out-cubic': cubicBezier( 0.645, 0.045, 0.355, 1 ),
|
||||
|
||||
// quart
|
||||
'ease-in-quart': cubicBezier( 0.895, 0.03, 0.685, 0.22 ),
|
||||
'ease-out-quart': cubicBezier( 0.165, 0.84, 0.44, 1 ),
|
||||
'ease-in-out-quart': cubicBezier( 0.77, 0, 0.175, 1 ),
|
||||
|
||||
// quint
|
||||
'ease-in-quint': cubicBezier( 0.755, 0.05, 0.855, 0.06 ),
|
||||
'ease-out-quint': cubicBezier( 0.23, 1, 0.32, 1 ),
|
||||
'ease-in-out-quint': cubicBezier( 0.86, 0, 0.07, 1 ),
|
||||
|
||||
// expo
|
||||
'ease-in-expo': cubicBezier( 0.95, 0.05, 0.795, 0.035 ),
|
||||
'ease-out-expo': cubicBezier( 0.19, 1, 0.22, 1 ),
|
||||
'ease-in-out-expo': cubicBezier( 1, 0, 0, 1 ),
|
||||
|
||||
// circ
|
||||
'ease-in-circ': cubicBezier( 0.6, 0.04, 0.98, 0.335 ),
|
||||
'ease-out-circ': cubicBezier( 0.075, 0.82, 0.165, 1 ),
|
||||
'ease-in-out-circ': cubicBezier( 0.785, 0.135, 0.15, 0.86 ),
|
||||
|
||||
|
||||
// user param easings...
|
||||
|
||||
'spring': function( tension, friction, duration ){
|
||||
if( duration === 0 ){ // can't get a spring w/ duration 0
|
||||
return easings.linear; // duration 0 => jump to end so impl doesn't matter
|
||||
}
|
||||
|
||||
let spring = generateSpringRK4( tension, friction, duration );
|
||||
|
||||
return function( start, end, percent ){
|
||||
return start + (end - start) * spring( percent );
|
||||
};
|
||||
},
|
||||
|
||||
'cubic-bezier': cubicBezier
|
||||
};
|
||||
|
||||
export default easings;
|
||||
522
frontend/node_modules/cytoscape/src/core/index.mjs
generated
vendored
Normal file
522
frontend/node_modules/cytoscape/src/core/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,522 @@
|
||||
import window from '../window.mjs';
|
||||
import * as util from '../util/index.mjs';
|
||||
import Collection from '../collection/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import Promise from '../promise.mjs';
|
||||
|
||||
import addRemove from './add-remove.mjs';
|
||||
import animation from './animation/index.mjs';
|
||||
import events from './events.mjs';
|
||||
import exportFormat from './export.mjs';
|
||||
import layout from './layout.mjs';
|
||||
import notification from './notification.mjs';
|
||||
import renderer from './renderer.mjs';
|
||||
import search from './search.mjs';
|
||||
import style from './style.mjs';
|
||||
import viewport from './viewport.mjs';
|
||||
import data from './data.mjs';
|
||||
|
||||
let Core = function( opts ){
|
||||
let cy = this;
|
||||
|
||||
opts = util.extend( {}, opts );
|
||||
|
||||
let container = opts.container;
|
||||
|
||||
// allow for passing a wrapped jquery object
|
||||
// e.g. cytoscape({ container: $('#cy') })
|
||||
if( container && !is.htmlElement( container ) && is.htmlElement( container[0] ) ){
|
||||
container = container[0];
|
||||
}
|
||||
|
||||
let reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
|
||||
reg = reg || {};
|
||||
|
||||
if( reg && reg.cy ){
|
||||
reg.cy.destroy();
|
||||
|
||||
reg = {}; // old instance => replace reg completely
|
||||
}
|
||||
|
||||
let readies = reg.readies = reg.readies || [];
|
||||
|
||||
if( container ){ container._cyreg = reg; } // make sure container assoc'd reg points to this cy
|
||||
reg.cy = cy;
|
||||
|
||||
let head = window !== undefined && container !== undefined && !opts.headless;
|
||||
let options = opts;
|
||||
options.layout = util.extend( { name: head ? 'grid' : 'null' }, options.layout );
|
||||
options.renderer = util.extend( { name: head ? 'canvas' : 'null' }, options.renderer );
|
||||
|
||||
let defVal = function( def, val, altVal ){
|
||||
if( val !== undefined ){
|
||||
return val;
|
||||
} else if( altVal !== undefined ){
|
||||
return altVal;
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
};
|
||||
|
||||
let _p = this._private = {
|
||||
container: container, // html dom ele container
|
||||
ready: false, // whether ready has been triggered
|
||||
options: options, // cached options
|
||||
elements: new Collection( this ), // elements in the graph
|
||||
listeners: [], // list of listeners
|
||||
aniEles: new Collection( this ), // elements being animated
|
||||
data: options.data || {}, // data for the core
|
||||
scratch: {}, // scratch object for core
|
||||
layout: null,
|
||||
renderer: null,
|
||||
destroyed: false, // whether destroy was called
|
||||
notificationsEnabled: true, // whether notifications are sent to the renderer
|
||||
minZoom: 1e-50,
|
||||
maxZoom: 1e50,
|
||||
zoomingEnabled: defVal( true, options.zoomingEnabled ),
|
||||
userZoomingEnabled: defVal( true, options.userZoomingEnabled ),
|
||||
panningEnabled: defVal( true, options.panningEnabled ),
|
||||
userPanningEnabled: defVal( true, options.userPanningEnabled ),
|
||||
boxSelectionEnabled: defVal( true, options.boxSelectionEnabled ),
|
||||
autolock: defVal( false, options.autolock, options.autolockNodes ),
|
||||
autoungrabify: defVal( false, options.autoungrabify, options.autoungrabifyNodes ),
|
||||
autounselectify: defVal( false, options.autounselectify ),
|
||||
styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
|
||||
zoom: is.number( options.zoom ) ? options.zoom : 1,
|
||||
pan: {
|
||||
x: is.plainObject( options.pan ) && is.number( options.pan.x ) ? options.pan.x : 0,
|
||||
y: is.plainObject( options.pan ) && is.number( options.pan.y ) ? options.pan.y : 0
|
||||
},
|
||||
animation: { // object for currently-running animations
|
||||
current: [],
|
||||
queue: []
|
||||
},
|
||||
hasCompoundNodes: false,
|
||||
multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)
|
||||
};
|
||||
|
||||
this.createEmitter();
|
||||
|
||||
// set selection type
|
||||
this.selectionType( options.selectionType );
|
||||
|
||||
// init zoom bounds
|
||||
this.zoomRange({ min: options.minZoom, max: options.maxZoom });
|
||||
|
||||
let loadExtData = function( extData, next ){
|
||||
let anyIsPromise = extData.some( is.promise );
|
||||
|
||||
if( anyIsPromise ){
|
||||
return Promise.all( extData ).then( next ); // load all data asynchronously, then exec rest of init
|
||||
} else {
|
||||
next( extData ); // exec synchronously for convenience
|
||||
}
|
||||
};
|
||||
|
||||
// start with the default stylesheet so we have something before loading an external stylesheet
|
||||
if( _p.styleEnabled ){
|
||||
cy.setStyle([]);
|
||||
}
|
||||
|
||||
// create the renderer
|
||||
let rendererOptions = util.assign({}, options, options.renderer); // allow rendering hints in top level options
|
||||
cy.initRenderer( rendererOptions );
|
||||
|
||||
let setElesAndLayout = function( elements, onload, ondone ){
|
||||
cy.notifications( false );
|
||||
|
||||
// remove old elements
|
||||
let oldEles = cy.mutableElements();
|
||||
if( oldEles.length > 0 ){
|
||||
oldEles.remove();
|
||||
}
|
||||
|
||||
if( elements != null ){
|
||||
if( is.plainObject( elements ) || is.array( elements ) ){
|
||||
cy.add( elements );
|
||||
}
|
||||
}
|
||||
|
||||
cy.one( 'layoutready', function( e ){
|
||||
cy.notifications( true );
|
||||
cy.emit( e ); // we missed this event by turning notifications off, so pass it on
|
||||
|
||||
cy.one( 'load', onload );
|
||||
cy.emitAndNotify( 'load' );
|
||||
} ).one( 'layoutstop', function(){
|
||||
cy.one( 'done', ondone );
|
||||
cy.emit( 'done' );
|
||||
} );
|
||||
|
||||
let layoutOpts = util.extend( {}, cy._private.options.layout );
|
||||
layoutOpts.eles = cy.elements();
|
||||
|
||||
cy.layout( layoutOpts ).run();
|
||||
};
|
||||
|
||||
loadExtData([ options.style, options.elements ], function( thens ){
|
||||
let initStyle = thens[0];
|
||||
let initEles = thens[1];
|
||||
|
||||
// init style
|
||||
if( _p.styleEnabled ){
|
||||
cy.style().append( initStyle );
|
||||
}
|
||||
|
||||
// initial load
|
||||
setElesAndLayout( initEles, function(){ // onready
|
||||
cy.startAnimationLoop();
|
||||
_p.ready = true;
|
||||
|
||||
// if a ready callback is specified as an option, the bind it
|
||||
if( is.fn( options.ready ) ){
|
||||
cy.on( 'ready', options.ready );
|
||||
}
|
||||
|
||||
// bind all the ready handlers registered before creating this instance
|
||||
for( let i = 0; i < readies.length; i++ ){
|
||||
let fn = readies[ i ];
|
||||
cy.on( 'ready', fn );
|
||||
}
|
||||
if( reg ){ reg.readies = []; } // clear b/c we've bound them all and don't want to keep it around in case a new core uses the same div etc
|
||||
|
||||
cy.emit( 'ready' );
|
||||
}, options.done );
|
||||
|
||||
} );
|
||||
};
|
||||
|
||||
let corefn = Core.prototype; // short alias
|
||||
|
||||
util.extend( corefn, {
|
||||
instanceString: function(){
|
||||
return 'core';
|
||||
},
|
||||
|
||||
isReady: function(){
|
||||
return this._private.ready;
|
||||
},
|
||||
|
||||
destroyed: function(){
|
||||
return this._private.destroyed;
|
||||
},
|
||||
|
||||
ready: function( fn ){
|
||||
if( this.isReady() ){
|
||||
this.emitter().emit( 'ready', [], fn ); // just calls fn as though triggered via ready event
|
||||
} else {
|
||||
this.on( 'ready', fn );
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
let cy = this;
|
||||
if( cy.destroyed() ) return;
|
||||
|
||||
cy.stopAnimationLoop();
|
||||
|
||||
cy.destroyRenderer();
|
||||
|
||||
this.emit( 'destroy' );
|
||||
|
||||
cy._private.destroyed = true;
|
||||
|
||||
return cy;
|
||||
},
|
||||
|
||||
hasElementWithId: function( id ){
|
||||
return this._private.elements.hasElementWithId( id );
|
||||
},
|
||||
|
||||
getElementById: function( id ){
|
||||
return this._private.elements.getElementById( id );
|
||||
},
|
||||
|
||||
hasCompoundNodes: function(){
|
||||
return this._private.hasCompoundNodes;
|
||||
},
|
||||
|
||||
headless: function(){
|
||||
return this._private.renderer.isHeadless();
|
||||
},
|
||||
|
||||
styleEnabled: function(){
|
||||
return this._private.styleEnabled;
|
||||
},
|
||||
|
||||
addToPool: function( eles ){
|
||||
this._private.elements.merge( eles );
|
||||
|
||||
return this; // chaining
|
||||
},
|
||||
|
||||
removeFromPool: function( eles ){
|
||||
this._private.elements.unmerge( eles );
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
container: function(){
|
||||
return this._private.container || null;
|
||||
},
|
||||
|
||||
window: function() {
|
||||
let container = this._private.container;
|
||||
if (container == null) return window;
|
||||
|
||||
let ownerDocument = this._private.container.ownerDocument;
|
||||
|
||||
if (ownerDocument === undefined || ownerDocument == null) {
|
||||
return window;
|
||||
}
|
||||
|
||||
return ownerDocument.defaultView || window;
|
||||
},
|
||||
|
||||
mount: function( container ){
|
||||
if( container == null ){ return; }
|
||||
|
||||
let cy = this;
|
||||
let _p = cy._private;
|
||||
let options = _p.options;
|
||||
|
||||
if( !is.htmlElement( container ) && is.htmlElement( container[0] ) ){
|
||||
container = container[0];
|
||||
}
|
||||
|
||||
cy.stopAnimationLoop();
|
||||
|
||||
cy.destroyRenderer();
|
||||
|
||||
_p.container = container;
|
||||
_p.styleEnabled = true;
|
||||
|
||||
cy.invalidateSize();
|
||||
|
||||
cy.initRenderer( util.assign({}, options, options.renderer, {
|
||||
// allow custom renderer name to be re-used, otherwise use canvas
|
||||
name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
|
||||
}) );
|
||||
|
||||
cy.startAnimationLoop();
|
||||
|
||||
cy.style( options.style );
|
||||
|
||||
cy.emit( 'mount' );
|
||||
|
||||
return cy;
|
||||
},
|
||||
|
||||
unmount: function(){
|
||||
let cy = this;
|
||||
|
||||
cy.stopAnimationLoop();
|
||||
|
||||
cy.destroyRenderer();
|
||||
|
||||
cy.initRenderer( { name: 'null' } );
|
||||
|
||||
cy.emit( 'unmount' );
|
||||
|
||||
return cy;
|
||||
},
|
||||
|
||||
options: function(){
|
||||
return util.copy( this._private.options );
|
||||
},
|
||||
|
||||
json: function( obj ){
|
||||
let cy = this;
|
||||
let _p = cy._private;
|
||||
let eles = cy.mutableElements();
|
||||
let getFreshRef = ele => cy.getElementById(ele.id());
|
||||
|
||||
if( is.plainObject( obj ) ){ // set
|
||||
|
||||
cy.startBatch();
|
||||
|
||||
if( obj.elements ){
|
||||
let idInJson = {};
|
||||
|
||||
let updateEles = function( jsons, gr ){
|
||||
let toAdd = [];
|
||||
let toMod = [];
|
||||
|
||||
for( let i = 0; i < jsons.length; i++ ){
|
||||
let json = jsons[ i ];
|
||||
|
||||
if( !json.data.id ){
|
||||
util.warn( 'cy.json() cannot handle elements without an ID attribute' );
|
||||
continue;
|
||||
}
|
||||
|
||||
let id = '' + json.data.id; // id must be string
|
||||
let ele = cy.getElementById( id );
|
||||
|
||||
idInJson[ id ] = true;
|
||||
|
||||
if( ele.length !== 0 ){ // existing element should be updated
|
||||
toMod.push({ ele, json });
|
||||
} else { // otherwise should be added
|
||||
if( gr ){
|
||||
json.group = gr;
|
||||
|
||||
toAdd.push( json );
|
||||
} else {
|
||||
toAdd.push( json );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cy.add( toAdd );
|
||||
|
||||
for( let i = 0; i < toMod.length; i++ ){
|
||||
let { ele, json } = toMod[i];
|
||||
|
||||
ele.json(json);
|
||||
}
|
||||
};
|
||||
|
||||
if( is.array( obj.elements ) ){ // elements: []
|
||||
updateEles( obj.elements );
|
||||
|
||||
} else { // elements: { nodes: [], edges: [] }
|
||||
let grs = [ 'nodes', 'edges' ];
|
||||
for( let i = 0; i < grs.length; i++ ){
|
||||
let gr = grs[ i ];
|
||||
let elements = obj.elements[ gr ];
|
||||
|
||||
if( is.array( elements ) ){
|
||||
updateEles( elements, gr );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let parentsToRemove = cy.collection();
|
||||
|
||||
(eles
|
||||
.filter(ele => !idInJson[ ele.id() ])
|
||||
.forEach(ele => {
|
||||
if ( ele.isParent() ) {
|
||||
parentsToRemove.merge(ele);
|
||||
} else {
|
||||
ele.remove();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// so that children are not removed w/parent
|
||||
parentsToRemove.forEach(ele => ele.children().move({ parent: null }));
|
||||
|
||||
// intermediate parents may be moved by prior line, so make sure we remove by fresh refs
|
||||
parentsToRemove.forEach(ele => getFreshRef(ele).remove());
|
||||
}
|
||||
|
||||
if( obj.style ){
|
||||
cy.style( obj.style );
|
||||
}
|
||||
|
||||
if( obj.zoom != null && obj.zoom !== _p.zoom ){
|
||||
cy.zoom( obj.zoom );
|
||||
}
|
||||
|
||||
if( obj.pan ){
|
||||
if( obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y ){
|
||||
cy.pan( obj.pan );
|
||||
}
|
||||
}
|
||||
|
||||
if( obj.data ){
|
||||
cy.data( obj.data );
|
||||
}
|
||||
|
||||
let fields = [
|
||||
'minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled',
|
||||
'panningEnabled', 'userPanningEnabled',
|
||||
'boxSelectionEnabled',
|
||||
'autolock', 'autoungrabify', 'autounselectify',
|
||||
'multiClickDebounceTime'
|
||||
];
|
||||
|
||||
for( let i = 0; i < fields.length; i++ ){
|
||||
let f = fields[ i ];
|
||||
|
||||
if( obj[ f ] != null ){
|
||||
cy[ f ]( obj[ f ] );
|
||||
}
|
||||
}
|
||||
|
||||
cy.endBatch();
|
||||
|
||||
return this; // chaining
|
||||
} else { // get
|
||||
let flat = !!obj;
|
||||
let json = {};
|
||||
|
||||
if( flat ){
|
||||
json.elements = this.elements().map( ele => ele.json() );
|
||||
} else {
|
||||
json.elements = {};
|
||||
|
||||
eles.forEach( function( ele ){
|
||||
let group = ele.group();
|
||||
|
||||
if( !json.elements[ group ] ){
|
||||
json.elements[ group ] = [];
|
||||
}
|
||||
|
||||
json.elements[ group ].push( ele.json() );
|
||||
} );
|
||||
}
|
||||
|
||||
if( this._private.styleEnabled ){
|
||||
json.style = cy.style().json();
|
||||
}
|
||||
|
||||
json.data = util.copy( cy.data() );
|
||||
|
||||
let options = _p.options;
|
||||
|
||||
json.zoomingEnabled = _p.zoomingEnabled;
|
||||
json.userZoomingEnabled = _p.userZoomingEnabled;
|
||||
json.zoom = _p.zoom;
|
||||
json.minZoom = _p.minZoom;
|
||||
json.maxZoom = _p.maxZoom;
|
||||
json.panningEnabled = _p.panningEnabled;
|
||||
json.userPanningEnabled = _p.userPanningEnabled;
|
||||
json.pan = util.copy( _p.pan );
|
||||
json.boxSelectionEnabled = _p.boxSelectionEnabled;
|
||||
json.renderer = util.copy( options.renderer );
|
||||
json.hideEdgesOnViewport = options.hideEdgesOnViewport;
|
||||
json.textureOnViewport = options.textureOnViewport;
|
||||
json.wheelSensitivity = options.wheelSensitivity;
|
||||
json.motionBlur = options.motionBlur;
|
||||
json.multiClickDebounceTime = options.multiClickDebounceTime;
|
||||
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
corefn.$id = corefn.getElementById;
|
||||
|
||||
[
|
||||
addRemove,
|
||||
animation,
|
||||
events,
|
||||
exportFormat,
|
||||
layout,
|
||||
notification,
|
||||
renderer,
|
||||
search,
|
||||
style,
|
||||
viewport,
|
||||
data
|
||||
].forEach( function( props ){
|
||||
util.extend( corefn, props );
|
||||
} );
|
||||
|
||||
export default Core;
|
||||
208
frontend/node_modules/cytoscape/src/define/data.mjs
generated
vendored
Normal file
208
frontend/node_modules/cytoscape/src/define/data.mjs
generated
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
import * as util from '../util/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import get from 'lodash/get.js';
|
||||
import set from 'lodash/set.js';
|
||||
import toPath from 'lodash/toPath.js';
|
||||
|
||||
let define = {
|
||||
|
||||
// access data field
|
||||
data: function( params ){
|
||||
let defaults = {
|
||||
field: 'data',
|
||||
bindingEvent: 'data',
|
||||
allowBinding: false,
|
||||
allowSetting: false,
|
||||
allowGetting: false,
|
||||
settingEvent: 'data',
|
||||
settingTriggersEvent: false,
|
||||
triggerFnName: 'trigger',
|
||||
immutableKeys: {}, // key => true if immutable
|
||||
updateStyle: false,
|
||||
beforeGet: function( self ){},
|
||||
beforeSet: function( self, obj ){},
|
||||
onSet: function( self ){},
|
||||
canSet: function( self ){ return true; }
|
||||
};
|
||||
params = util.extend( {}, defaults, params );
|
||||
|
||||
return function dataImpl( name, value ){
|
||||
let p = params;
|
||||
let self = this;
|
||||
let selfIsArrayLike = self.length !== undefined;
|
||||
let all = selfIsArrayLike ? self : [ self ]; // put in array if not array-like
|
||||
let single = selfIsArrayLike ? self[0] : self;
|
||||
|
||||
// .data('foo', ...)
|
||||
if (is.string(name)) { // set or get property
|
||||
let isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
|
||||
let path = isPathLike && toPath(name);
|
||||
|
||||
// .data('foo')
|
||||
if( p.allowGetting && value === undefined ){ // get
|
||||
|
||||
let ret;
|
||||
if( single ){
|
||||
p.beforeGet( single );
|
||||
|
||||
// check if it's path and a field with the same name doesn't exist
|
||||
if (path && single._private[ p.field ][ name ] === undefined) {
|
||||
ret = get(single._private[ p.field ], path);
|
||||
} else {
|
||||
ret = single._private[ p.field ][ name ];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
// .data('foo', 'bar')
|
||||
} else if( p.allowSetting && value !== undefined ){ // set
|
||||
let valid = !p.immutableKeys[ name ];
|
||||
if( valid ){
|
||||
let change = { [name]: value };
|
||||
|
||||
p.beforeSet( self, change );
|
||||
|
||||
for( let i = 0, l = all.length; i < l; i++ ){
|
||||
let ele = all[i];
|
||||
|
||||
if( p.canSet( ele ) ){
|
||||
if (path && single._private[ p.field ][ name ] === undefined) {
|
||||
set(ele._private[ p.field ], path, value);
|
||||
} else {
|
||||
ele._private[ p.field ][ name ] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update mappers if asked
|
||||
if( p.updateStyle ){ self.updateStyle(); }
|
||||
|
||||
// call onSet callback
|
||||
p.onSet( self );
|
||||
|
||||
if( p.settingTriggersEvent ){
|
||||
self[ p.triggerFnName ]( p.settingEvent );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .data({ 'foo': 'bar' })
|
||||
} else if( p.allowSetting && is.plainObject( name ) ){ // extend
|
||||
let obj = name;
|
||||
let k, v;
|
||||
let keys = Object.keys( obj );
|
||||
|
||||
p.beforeSet( self, obj );
|
||||
|
||||
for( let i = 0; i < keys.length; i++ ){
|
||||
k = keys[ i ];
|
||||
v = obj[ k ];
|
||||
|
||||
let valid = !p.immutableKeys[ k ];
|
||||
if( valid ){
|
||||
for( let j = 0; j < all.length; j++ ){
|
||||
let ele = all[j];
|
||||
|
||||
if( p.canSet( ele ) ){
|
||||
ele._private[ p.field ][ k ] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update mappers if asked
|
||||
if( p.updateStyle ){ self.updateStyle(); }
|
||||
|
||||
// call onSet callback
|
||||
p.onSet( self );
|
||||
|
||||
if( p.settingTriggersEvent ){
|
||||
self[ p.triggerFnName ]( p.settingEvent );
|
||||
}
|
||||
|
||||
// .data(function(){ ... })
|
||||
} else if( p.allowBinding && is.fn( name ) ){ // bind to event
|
||||
let fn = name;
|
||||
self.on( p.bindingEvent, fn );
|
||||
|
||||
// .data()
|
||||
} else if( p.allowGetting && name === undefined ){ // get whole object
|
||||
let ret;
|
||||
if( single ){
|
||||
p.beforeGet( single );
|
||||
|
||||
ret = single._private[ p.field ];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
return self; // maintain chainability
|
||||
}; // function
|
||||
}, // data
|
||||
|
||||
// remove data field
|
||||
removeData: function( params ){
|
||||
let defaults = {
|
||||
field: 'data',
|
||||
event: 'data',
|
||||
triggerFnName: 'trigger',
|
||||
triggerEvent: false,
|
||||
immutableKeys: {} // key => true if immutable
|
||||
};
|
||||
params = util.extend( {}, defaults, params );
|
||||
|
||||
return function removeDataImpl( names ){
|
||||
let p = params;
|
||||
let self = this;
|
||||
let selfIsArrayLike = self.length !== undefined;
|
||||
let all = selfIsArrayLike ? self : [ self ]; // put in array if not array-like
|
||||
|
||||
// .removeData('foo bar')
|
||||
if( is.string( names ) ){ // then get the list of keys, and delete them
|
||||
let keys = names.split( /\s+/ );
|
||||
let l = keys.length;
|
||||
|
||||
for( let i = 0; i < l; i++ ){ // delete each non-empty key
|
||||
let key = keys[ i ];
|
||||
if( is.emptyString( key ) ){ continue; }
|
||||
|
||||
let valid = !p.immutableKeys[ key ]; // not valid if immutable
|
||||
if( valid ){
|
||||
for( let i_a = 0, l_a = all.length; i_a < l_a; i_a++ ){
|
||||
all[ i_a ]._private[ p.field ][ key ] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( p.triggerEvent ){
|
||||
self[ p.triggerFnName ]( p.event );
|
||||
}
|
||||
|
||||
// .removeData()
|
||||
} else if( names === undefined ){ // then delete all keys
|
||||
|
||||
for( let i_a = 0, l_a = all.length; i_a < l_a; i_a++ ){
|
||||
let _privateFields = all[ i_a ]._private[ p.field ];
|
||||
let keys = Object.keys( _privateFields );
|
||||
|
||||
for( let i = 0; i < keys.length; i++ ){
|
||||
let key = keys[i];
|
||||
let validKeyToDelete = !p.immutableKeys[ key ];
|
||||
|
||||
if( validKeyToDelete ){
|
||||
_privateFields[ key ] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( p.triggerEvent ){
|
||||
self[ p.triggerFnName ]( p.event );
|
||||
}
|
||||
}
|
||||
|
||||
return self; // maintain chaining
|
||||
}; // function
|
||||
}, // removeData
|
||||
}; // define
|
||||
|
||||
export default define;
|
||||
111
frontend/node_modules/cytoscape/src/event.mjs
generated
vendored
Normal file
111
frontend/node_modules/cytoscape/src/event.mjs
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
/*!
|
||||
Event object based on jQuery events, MIT license
|
||||
|
||||
https://jquery.org/license/
|
||||
https://tldrlegal.com/license/mit-license
|
||||
https://github.com/jquery/jquery/blob/master/src/event.js
|
||||
*/
|
||||
|
||||
let Event = function( src, props ){
|
||||
this.recycle( src, props );
|
||||
};
|
||||
|
||||
function returnFalse(){
|
||||
return false;
|
||||
}
|
||||
|
||||
function returnTrue(){
|
||||
return true;
|
||||
}
|
||||
|
||||
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
|
||||
Event.prototype = {
|
||||
instanceString: function(){
|
||||
return 'event';
|
||||
},
|
||||
|
||||
recycle: function( src, props ){
|
||||
this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
|
||||
|
||||
if( src != null && src.preventDefault ){ // Browser Event object
|
||||
this.type = src.type;
|
||||
|
||||
// Events bubbling up the document may have been marked as prevented
|
||||
// by a handler lower down the tree; reflect the correct value.
|
||||
this.isDefaultPrevented = ( src.defaultPrevented ) ? returnTrue : returnFalse;
|
||||
|
||||
} else if( src != null && src.type ){ // Plain object containing all event details
|
||||
props = src;
|
||||
|
||||
} else { // Event string
|
||||
this.type = src;
|
||||
}
|
||||
|
||||
// Put explicitly provided properties onto the event object
|
||||
if( props != null ){
|
||||
// more efficient to manually copy fields we use
|
||||
this.originalEvent = props.originalEvent;
|
||||
this.type = props.type != null ? props.type : this.type;
|
||||
this.cy = props.cy;
|
||||
this.target = props.target;
|
||||
this.position = props.position;
|
||||
this.renderedPosition = props.renderedPosition;
|
||||
this.namespace = props.namespace;
|
||||
this.layout = props.layout;
|
||||
}
|
||||
|
||||
if( this.cy != null && this.position != null && this.renderedPosition == null ){
|
||||
// create a rendered position based on the passed position
|
||||
let pos = this.position;
|
||||
let zoom = this.cy.zoom();
|
||||
let pan = this.cy.pan();
|
||||
|
||||
this.renderedPosition = {
|
||||
x: pos.x * zoom + pan.x,
|
||||
y: pos.y * zoom + pan.y
|
||||
};
|
||||
}
|
||||
|
||||
// Create a timestamp if incoming event doesn't have one
|
||||
this.timeStamp = src && src.timeStamp || Date.now();
|
||||
},
|
||||
|
||||
preventDefault: function(){
|
||||
this.isDefaultPrevented = returnTrue;
|
||||
|
||||
let e = this.originalEvent;
|
||||
if( !e ){
|
||||
return;
|
||||
}
|
||||
|
||||
// if preventDefault exists run it on the original event
|
||||
if( e.preventDefault ){
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
stopPropagation: function(){
|
||||
this.isPropagationStopped = returnTrue;
|
||||
|
||||
let e = this.originalEvent;
|
||||
if( !e ){
|
||||
return;
|
||||
}
|
||||
|
||||
// if stopPropagation exists run it on the original event
|
||||
if( e.stopPropagation ){
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
stopImmediatePropagation: function(){
|
||||
this.isImmediatePropagationStopped = returnTrue;
|
||||
this.stopPropagation();
|
||||
},
|
||||
|
||||
isDefaultPrevented: returnFalse,
|
||||
isPropagationStopped: returnFalse,
|
||||
isImmediatePropagationStopped: returnFalse
|
||||
};
|
||||
|
||||
export default Event;
|
||||
246
frontend/node_modules/cytoscape/src/extension.mjs
generated
vendored
Normal file
246
frontend/node_modules/cytoscape/src/extension.mjs
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
import * as util from'./util/index.mjs';
|
||||
import define from './define/index.mjs';
|
||||
import Collection from './collection/index.mjs';
|
||||
import Core from './core/index.mjs';
|
||||
import incExts from './extensions/index.mjs';
|
||||
import * as is from './is.mjs';
|
||||
import Emitter from './emitter.mjs';
|
||||
|
||||
// registered extensions to cytoscape, indexed by name
|
||||
let extensions = {};
|
||||
|
||||
// registered modules for extensions, indexed by name
|
||||
let modules = {};
|
||||
|
||||
function setExtension( type, name, registrant ){
|
||||
|
||||
let ext = registrant;
|
||||
|
||||
let overrideErr = function( field ){
|
||||
util.warn( 'Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden' );
|
||||
};
|
||||
|
||||
if( type === 'core' ){
|
||||
if( Core.prototype[ name ] ){
|
||||
return overrideErr( name );
|
||||
} else {
|
||||
Core.prototype[ name ] = registrant;
|
||||
}
|
||||
|
||||
} else if( type === 'collection' ){
|
||||
if( Collection.prototype[ name ] ){
|
||||
return overrideErr( name );
|
||||
} else {
|
||||
Collection.prototype[ name ] = registrant;
|
||||
}
|
||||
|
||||
} else if( type === 'layout' ){
|
||||
// fill in missing layout functions in the prototype
|
||||
|
||||
let Layout = function( options ){
|
||||
this.options = options;
|
||||
|
||||
registrant.call( this, options );
|
||||
|
||||
// make sure layout has _private for use w/ std apis like .on()
|
||||
if( !is.plainObject( this._private ) ){
|
||||
this._private = {};
|
||||
}
|
||||
|
||||
this._private.cy = options.cy;
|
||||
this._private.listeners = [];
|
||||
|
||||
this.createEmitter();
|
||||
};
|
||||
|
||||
let layoutProto = Layout.prototype = Object.create( registrant.prototype );
|
||||
|
||||
let optLayoutFns = [];
|
||||
|
||||
for( let i = 0; i < optLayoutFns.length; i++ ){
|
||||
let fnName = optLayoutFns[ i ];
|
||||
|
||||
layoutProto[ fnName ] = layoutProto[ fnName ] || function(){ return this; };
|
||||
}
|
||||
|
||||
// either .start() or .run() is defined, so autogen the other
|
||||
if( layoutProto.start && !layoutProto.run ){
|
||||
layoutProto.run = function(){ this.start(); return this; };
|
||||
} else if( !layoutProto.start && layoutProto.run ){
|
||||
layoutProto.start = function(){ this.run(); return this; };
|
||||
}
|
||||
|
||||
let regStop = registrant.prototype.stop;
|
||||
layoutProto.stop = function(){
|
||||
let opts = this.options;
|
||||
|
||||
if( opts && opts.animate ){
|
||||
let anis = this.animations;
|
||||
|
||||
if( anis ){
|
||||
for( let i = 0; i < anis.length; i++ ){
|
||||
anis[ i ].stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( regStop ){
|
||||
regStop.call( this );
|
||||
} else {
|
||||
this.emit( 'layoutstop' );
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
if( !layoutProto.destroy ){
|
||||
layoutProto.destroy = function(){
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
layoutProto.cy = function(){
|
||||
return this._private.cy;
|
||||
};
|
||||
|
||||
let getCy = layout => layout._private.cy;
|
||||
|
||||
let emitterOpts = {
|
||||
addEventFields: function( layout, evt ){
|
||||
evt.layout = layout;
|
||||
evt.cy = getCy(layout);
|
||||
evt.target = layout;
|
||||
},
|
||||
bubble: function(){ return true; },
|
||||
parent: function( layout ){ return getCy(layout); }
|
||||
};
|
||||
|
||||
util.assign( layoutProto, {
|
||||
createEmitter: function(){
|
||||
this._private.emitter = new Emitter( emitterOpts, this );
|
||||
|
||||
return this;
|
||||
},
|
||||
emitter: function(){ return this._private.emitter; },
|
||||
on: function( evt, cb ){ this.emitter().on( evt, cb ); return this; },
|
||||
one: function( evt, cb ){ this.emitter().one( evt, cb ); return this; },
|
||||
once: function( evt, cb ){ this.emitter().one( evt, cb ); return this; },
|
||||
removeListener: function( evt, cb ){ this.emitter().removeListener( evt, cb ); return this; },
|
||||
removeAllListeners: function(){ this.emitter().removeAllListeners(); return this; },
|
||||
emit: function( evt, params ){ this.emitter().emit( evt, params ); return this; }
|
||||
} );
|
||||
|
||||
define.eventAliasesOn( layoutProto );
|
||||
|
||||
ext = Layout; // replace with our wrapped layout
|
||||
|
||||
} else if( type === 'renderer' && name !== 'null' && name !== 'base' ){
|
||||
// user registered renderers inherit from base
|
||||
|
||||
let BaseRenderer = getExtension( 'renderer', 'base' );
|
||||
let bProto = BaseRenderer.prototype;
|
||||
let RegistrantRenderer = registrant;
|
||||
let rProto = registrant.prototype;
|
||||
|
||||
let Renderer = function(){
|
||||
BaseRenderer.apply( this, arguments );
|
||||
RegistrantRenderer.apply( this, arguments );
|
||||
};
|
||||
|
||||
let proto = Renderer.prototype;
|
||||
|
||||
for( let pName in bProto ){
|
||||
let pVal = bProto[ pName ];
|
||||
let existsInR = rProto[ pName ] != null;
|
||||
|
||||
if( existsInR ){
|
||||
return overrideErr( pName );
|
||||
}
|
||||
|
||||
proto[ pName ] = pVal; // take impl from base
|
||||
}
|
||||
|
||||
for( let pName in rProto ){
|
||||
proto[ pName ] = rProto[ pName ]; // take impl from registrant
|
||||
}
|
||||
|
||||
bProto.clientFunctions.forEach( function( name ){
|
||||
proto[ name ] = proto[ name ] || function(){
|
||||
util.error( 'Renderer does not implement `renderer.' + name + '()` on its prototype' );
|
||||
};
|
||||
} );
|
||||
|
||||
ext = Renderer;
|
||||
|
||||
} else if (type === '__proto__' || type === 'constructor' || type === 'prototype'){
|
||||
// to avoid potential prototype pollution
|
||||
return util.error( type + ' is an illegal type to be registered, possibly lead to prototype pollutions' );
|
||||
}
|
||||
|
||||
return util.setMap( {
|
||||
map: extensions,
|
||||
keys: [ type, name ],
|
||||
value: ext
|
||||
} );
|
||||
}
|
||||
|
||||
function getExtension( type, name ){
|
||||
return util.getMap( {
|
||||
map: extensions,
|
||||
keys: [ type, name ]
|
||||
} );
|
||||
}
|
||||
|
||||
function setModule( type, name, moduleType, moduleName, registrant ){
|
||||
return util.setMap( {
|
||||
map: modules,
|
||||
keys: [ type, name, moduleType, moduleName ],
|
||||
value: registrant
|
||||
} );
|
||||
}
|
||||
|
||||
function getModule( type, name, moduleType, moduleName ){
|
||||
return util.getMap( {
|
||||
map: modules,
|
||||
keys: [ type, name, moduleType, moduleName ]
|
||||
} );
|
||||
}
|
||||
|
||||
let extension = function(){
|
||||
// e.g. extension('renderer', 'svg')
|
||||
if( arguments.length === 2 ){
|
||||
return getExtension.apply( null, arguments );
|
||||
}
|
||||
|
||||
// e.g. extension('renderer', 'svg', { ... })
|
||||
else if( arguments.length === 3 ){
|
||||
return setExtension.apply( null, arguments );
|
||||
}
|
||||
|
||||
// e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
|
||||
else if( arguments.length === 4 ){
|
||||
return getModule.apply( null, arguments );
|
||||
}
|
||||
|
||||
// e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
|
||||
else if( arguments.length === 5 ){
|
||||
return setModule.apply( null, arguments );
|
||||
}
|
||||
|
||||
else {
|
||||
util.error( 'Invalid extension access syntax' );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// allows a core instance to access extensions internally
|
||||
Core.prototype.extension = extension;
|
||||
|
||||
// included extensions
|
||||
incExts.forEach( function( group ){
|
||||
group.extensions.forEach( function( ext ){
|
||||
setExtension( group.type, ext.name, ext.impl );
|
||||
} );
|
||||
} );
|
||||
|
||||
export default extension;
|
||||
105
frontend/node_modules/cytoscape/src/extensions/layout/circle.mjs
generated
vendored
Normal file
105
frontend/node_modules/cytoscape/src/extensions/layout/circle.mjs
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
import * as util from '../../util/index.mjs';
|
||||
import * as math from '../../math.mjs';
|
||||
import * as is from '../../is.mjs';
|
||||
|
||||
let defaults = {
|
||||
fit: true, // whether to fit the viewport to the graph
|
||||
padding: 30, // the padding on fit
|
||||
boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
|
||||
avoidOverlap: true, // prevents node overlap, may overflow boundingBox and radius if not enough space
|
||||
nodeDimensionsIncludeLabels: false, // Excludes the label when calculating node bounding boxes for the layout algorithm
|
||||
spacingFactor: undefined, // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
|
||||
radius: undefined, // the radius of the circle
|
||||
startAngle: 3 / 2 * Math.PI, // where nodes start in radians
|
||||
sweep: undefined, // how many radians should be between the first and last node (defaults to full circle)
|
||||
clockwise: true, // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
|
||||
sort: undefined, // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
|
||||
animate: false, // whether to transition the node positions
|
||||
animationDuration: 500, // duration of animation in ms if enabled
|
||||
animationEasing: undefined, // easing of animation if enabled
|
||||
animateFilter: function ( node, i ){ return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
|
||||
ready: undefined, // callback on layoutready
|
||||
stop: undefined, // callback on layoutstop
|
||||
transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
|
||||
|
||||
};
|
||||
|
||||
function CircleLayout( options ){
|
||||
this.options = util.extend( {}, defaults, options );
|
||||
}
|
||||
|
||||
CircleLayout.prototype.run = function(){
|
||||
let params = this.options;
|
||||
let options = params;
|
||||
|
||||
let cy = params.cy;
|
||||
let eles = options.eles;
|
||||
|
||||
let clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
|
||||
|
||||
let nodes = eles.nodes().not( ':parent' );
|
||||
|
||||
if( options.sort ){
|
||||
nodes = nodes.sort( options.sort );
|
||||
}
|
||||
|
||||
let bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
|
||||
x1: 0, y1: 0, w: cy.width(), h: cy.height()
|
||||
} );
|
||||
|
||||
let center = {
|
||||
x: bb.x1 + bb.w / 2,
|
||||
y: bb.y1 + bb.h / 2
|
||||
};
|
||||
|
||||
let sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
|
||||
let dTheta = sweep / ( Math.max( 1, nodes.length - 1 ) );
|
||||
let r;
|
||||
|
||||
let minDistance = 0;
|
||||
for( let i = 0; i < nodes.length; i++ ){
|
||||
let n = nodes[ i ];
|
||||
let nbb = n.layoutDimensions( options );
|
||||
let w = nbb.w;
|
||||
let h = nbb.h;
|
||||
|
||||
minDistance = Math.max( minDistance, w, h );
|
||||
}
|
||||
|
||||
if( is.number( options.radius ) ){
|
||||
r = options.radius;
|
||||
} else if( nodes.length <= 1 ){
|
||||
r = 0;
|
||||
} else {
|
||||
r = Math.min( bb.h, bb.w ) / 2 - minDistance;
|
||||
}
|
||||
|
||||
// calculate the radius
|
||||
if( nodes.length > 1 && options.avoidOverlap ){ // but only if more than one node (can't overlap)
|
||||
minDistance *= 1.75; // just to have some nice spacing
|
||||
|
||||
let dcos = Math.cos( dTheta ) - Math.cos( 0 );
|
||||
let dsin = Math.sin( dTheta ) - Math.sin( 0 );
|
||||
let rMin = Math.sqrt( minDistance * minDistance / ( dcos * dcos + dsin * dsin ) ); // s.t. no nodes overlapping
|
||||
r = Math.max( rMin, r );
|
||||
}
|
||||
|
||||
let getPos = function( ele, i ){
|
||||
let theta = options.startAngle + i * dTheta * ( clockwise ? 1 : -1 );
|
||||
|
||||
let rx = r * Math.cos( theta );
|
||||
let ry = r * Math.sin( theta );
|
||||
let pos = {
|
||||
x: center.x + rx,
|
||||
y: center.y + ry
|
||||
};
|
||||
|
||||
return pos;
|
||||
};
|
||||
|
||||
eles.nodes().layoutPositions( this, options, getPos );
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
export default CircleLayout;
|
||||
1340
frontend/node_modules/cytoscape/src/extensions/layout/cose.mjs
generated
vendored
Normal file
1340
frontend/node_modules/cytoscape/src/extensions/layout/cose.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
43
frontend/node_modules/cytoscape/src/extensions/layout/random.mjs
generated
vendored
Normal file
43
frontend/node_modules/cytoscape/src/extensions/layout/random.mjs
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
import * as util from '../../util/index.mjs';
|
||||
import * as math from '../../math.mjs';
|
||||
|
||||
let defaults = {
|
||||
fit: true, // whether to fit to viewport
|
||||
padding: 30, // fit padding
|
||||
boundingBox: undefined, // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
|
||||
animate: false, // whether to transition the node positions
|
||||
animationDuration: 500, // duration of animation in ms if enabled
|
||||
animationEasing: undefined, // easing of animation if enabled
|
||||
animateFilter: function ( node, i ){ return true; }, // a function that determines whether the node should be animated. All nodes animated by default on animate enabled. Non-animated nodes are positioned immediately when the layout starts
|
||||
ready: undefined, // callback on layoutready
|
||||
stop: undefined, // callback on layoutstop
|
||||
transform: function (node, position ){ return position; } // transform a given node position. Useful for changing flow direction in discrete layouts
|
||||
};
|
||||
|
||||
function RandomLayout( options ){
|
||||
this.options = util.extend( {}, defaults, options );
|
||||
}
|
||||
|
||||
RandomLayout.prototype.run = function(){
|
||||
let options = this.options;
|
||||
let cy = options.cy;
|
||||
let eles = options.eles;
|
||||
|
||||
|
||||
let bb = math.makeBoundingBox( options.boundingBox ? options.boundingBox : {
|
||||
x1: 0, y1: 0, w: cy.width(), h: cy.height()
|
||||
} );
|
||||
|
||||
let getPos = function( node, i ){
|
||||
return {
|
||||
x: bb.x1 + Math.round( Math.random() * bb.w ),
|
||||
y: bb.y1 + Math.round( Math.random() * bb.h )
|
||||
};
|
||||
};
|
||||
|
||||
eles.nodes().layoutPositions( this, options, getPos );
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
export default RandomLayout;
|
||||
29
frontend/node_modules/cytoscape/src/extensions/renderer/base/coord-ele-math/index.mjs
generated
vendored
Normal file
29
frontend/node_modules/cytoscape/src/extensions/renderer/base/coord-ele-math/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
import * as util from '../../../../util/index.mjs';
|
||||
|
||||
import coords from './coords.mjs';
|
||||
import edgeArrows from './edge-arrows.mjs';
|
||||
import edgeControlPoints from './edge-control-points.mjs';
|
||||
import edgeEndpoints from './edge-endpoints.mjs';
|
||||
import edgeProjection from './edge-projection.mjs';
|
||||
import labels from './labels.mjs';
|
||||
import nodes from './nodes.mjs';
|
||||
import renderedStyle from './rendered-style.mjs';
|
||||
import zOrdering from './z-ordering.mjs';
|
||||
|
||||
var BRp = {};
|
||||
|
||||
[
|
||||
coords,
|
||||
edgeArrows,
|
||||
edgeControlPoints,
|
||||
edgeEndpoints,
|
||||
edgeProjection,
|
||||
labels,
|
||||
nodes,
|
||||
renderedStyle,
|
||||
zOrdering
|
||||
].forEach(function( props ){
|
||||
util.extend( BRp, props );
|
||||
});
|
||||
|
||||
export default BRp;
|
||||
38
frontend/node_modules/cytoscape/src/extensions/renderer/base/images.mjs
generated
vendored
Normal file
38
frontend/node_modules/cytoscape/src/extensions/renderer/base/images.mjs
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
var BRp = {};
|
||||
|
||||
BRp.getCachedImage = function( url, crossOrigin, onLoad ){
|
||||
var r = this;
|
||||
var imageCache = r.imageCache = r.imageCache || {};
|
||||
var cache = imageCache[ url ];
|
||||
|
||||
if( cache ){
|
||||
if( !cache.image.complete ){
|
||||
cache.image.addEventListener('load', onLoad);
|
||||
}
|
||||
|
||||
return cache.image;
|
||||
} else {
|
||||
cache = imageCache[ url ] = imageCache[ url ] || {};
|
||||
|
||||
var image = cache.image = new Image(); // eslint-disable-line no-undef
|
||||
|
||||
image.addEventListener('load', onLoad);
|
||||
image.addEventListener('error', function(){ image.error = true; });
|
||||
|
||||
// #1582 safari doesn't load data uris with crossOrigin properly
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=123978
|
||||
var dataUriPrefix = 'data:';
|
||||
var isDataUri = url.substring( 0, dataUriPrefix.length ).toLowerCase() === dataUriPrefix;
|
||||
if( !isDataUri ){
|
||||
// if crossorigin is 'null'(stringified), then manually set it to null
|
||||
crossOrigin = crossOrigin === 'null' ? null : crossOrigin;
|
||||
image.crossOrigin = crossOrigin; // prevent tainted canvas
|
||||
}
|
||||
|
||||
image.src = url;
|
||||
|
||||
return image;
|
||||
}
|
||||
};
|
||||
|
||||
export default BRp;
|
||||
224
frontend/node_modules/cytoscape/src/extensions/renderer/base/index.mjs
generated
vendored
Normal file
224
frontend/node_modules/cytoscape/src/extensions/renderer/base/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
import * as util from '../../../util/index.mjs';
|
||||
import * as is from '../../../is.mjs';
|
||||
|
||||
import arrowShapes from './arrow-shapes.mjs';
|
||||
import coordEleMath from './coord-ele-math/index.mjs';
|
||||
import images from './images.mjs';
|
||||
import loadListeners from './load-listeners.mjs';
|
||||
import nodeShapes from './node-shapes.mjs';
|
||||
import redraw from './redraw.mjs';
|
||||
|
||||
var BaseRenderer = function( options ){ this.init( options ); };
|
||||
var BR = BaseRenderer;
|
||||
var BRp = BR.prototype;
|
||||
|
||||
BRp.clientFunctions = [ 'redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl' ];
|
||||
|
||||
BRp.init = function( options ){
|
||||
var r = this;
|
||||
|
||||
r.options = options;
|
||||
|
||||
r.cy = options.cy;
|
||||
|
||||
var ctr = r.container = options.cy.container();
|
||||
var containerWindow = r.cy.window();
|
||||
|
||||
|
||||
// prepend a stylesheet in the head such that
|
||||
if( containerWindow ){
|
||||
var document = containerWindow.document;
|
||||
var head = document.head;
|
||||
var stylesheetId = '__________cytoscape_stylesheet';
|
||||
var className = '__________cytoscape_container';
|
||||
var stylesheetAlreadyExists = document.getElementById( stylesheetId ) != null;
|
||||
|
||||
if( ctr.className.indexOf( className ) < 0 ){
|
||||
ctr.className = ( ctr.className || '' ) + ' ' + className;
|
||||
}
|
||||
|
||||
if( !stylesheetAlreadyExists ){
|
||||
var stylesheet = document.createElement('style');
|
||||
|
||||
stylesheet.id = stylesheetId;
|
||||
stylesheet.textContent = '.'+className+' { position: relative; }';
|
||||
|
||||
head.insertBefore( stylesheet, head.children[0] ); // first so lowest priority
|
||||
}
|
||||
|
||||
var computedStyle = containerWindow.getComputedStyle( ctr );
|
||||
var position = computedStyle.getPropertyValue('position');
|
||||
|
||||
if( position === 'static' ){
|
||||
util.warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
|
||||
}
|
||||
}
|
||||
|
||||
r.selection = [ undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
|
||||
|
||||
r.bezierProjPcts = [ 0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95 ];
|
||||
|
||||
//--Pointer-related data
|
||||
r.hoverData = {down: null, last: null,
|
||||
downTime: null, triggerMode: null,
|
||||
dragging: false,
|
||||
initialPan: [ null, null ], capture: false};
|
||||
|
||||
r.dragData = {possibleDragElements: []};
|
||||
|
||||
r.touchData = {
|
||||
start: null, capture: false,
|
||||
|
||||
// These 3 fields related to tap, taphold events
|
||||
startPosition: [ null, null, null, null, null, null ],
|
||||
singleTouchStartTime: null,
|
||||
singleTouchMoved: true,
|
||||
|
||||
now: [ null, null, null, null, null, null ],
|
||||
earlier: [ null, null, null, null, null, null ]
|
||||
};
|
||||
|
||||
r.redraws = 0;
|
||||
r.showFps = options.showFps;
|
||||
r.debug = options.debug;
|
||||
r.webgl = options.webgl;
|
||||
|
||||
r.hideEdgesOnViewport = options.hideEdgesOnViewport;
|
||||
r.textureOnViewport = options.textureOnViewport;
|
||||
r.wheelSensitivity = options.wheelSensitivity;
|
||||
r.motionBlurEnabled = options.motionBlur; // on by default
|
||||
r.forcedPixelRatio = is.number(options.pixelRatio) ? options.pixelRatio : null;
|
||||
r.motionBlur = options.motionBlur; // for initial kick off
|
||||
r.motionBlurOpacity = options.motionBlurOpacity;
|
||||
r.motionBlurTransparency = 1 - r.motionBlurOpacity;
|
||||
r.motionBlurPxRatio = 1;
|
||||
r.mbPxRBlurry = 1; //0.8;
|
||||
r.minMbLowQualFrames = 4;
|
||||
r.fullQualityMb = false;
|
||||
r.clearedForMotionBlur = [];
|
||||
r.desktopTapThreshold = options.desktopTapThreshold;
|
||||
r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
|
||||
r.touchTapThreshold = options.touchTapThreshold;
|
||||
r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
|
||||
r.tapholdDuration = 500;
|
||||
|
||||
r.bindings = [];
|
||||
r.beforeRenderCallbacks = [];
|
||||
r.beforeRenderPriorities = { // higher priority execs before lower one
|
||||
animations: 400,
|
||||
eleCalcs: 300,
|
||||
eleTxrDeq: 200,
|
||||
lyrTxrDeq: 150,
|
||||
lyrTxrSkip: 100,
|
||||
};
|
||||
|
||||
r.registerNodeShapes();
|
||||
r.registerArrowShapes();
|
||||
r.registerCalculationListeners();
|
||||
};
|
||||
|
||||
BRp.notify = function( eventName, eles ) {
|
||||
var r = this;
|
||||
var cy = r.cy;
|
||||
|
||||
// the renderer can't be notified after it's destroyed
|
||||
if( this.destroyed ){ return; }
|
||||
|
||||
if( eventName === 'init' ){
|
||||
r.load();
|
||||
return;
|
||||
}
|
||||
|
||||
if( eventName === 'destroy' ){
|
||||
r.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if(
|
||||
eventName === 'add'
|
||||
|| eventName === 'remove'
|
||||
|| (eventName === 'move' && cy.hasCompoundNodes())
|
||||
|| eventName === 'load'
|
||||
|| eventName === 'zorder'
|
||||
|| eventName === 'mount'
|
||||
){
|
||||
r.invalidateCachedZSortedEles();
|
||||
}
|
||||
|
||||
if( eventName === 'viewport' ){
|
||||
r.redrawHint( 'select', true );
|
||||
}
|
||||
|
||||
if( eventName === 'gc' ){
|
||||
r.redrawHint( 'gc', true );
|
||||
}
|
||||
|
||||
if( eventName === 'load' || eventName === 'resize' || eventName === 'mount' ){
|
||||
r.invalidateContainerClientCoordsCache();
|
||||
r.matchCanvasSize( r.container );
|
||||
}
|
||||
|
||||
r.redrawHint( 'eles', true );
|
||||
r.redrawHint( 'drag', true );
|
||||
|
||||
this.startRenderLoop();
|
||||
|
||||
this.redraw();
|
||||
};
|
||||
|
||||
BRp.destroy = function(){
|
||||
var r = this;
|
||||
|
||||
r.destroyed = true;
|
||||
|
||||
r.cy.stopAnimationLoop();
|
||||
|
||||
for( var i = 0; i < r.bindings.length; i++ ){
|
||||
var binding = r.bindings[ i ];
|
||||
var b = binding;
|
||||
var tgt = b.target;
|
||||
|
||||
( tgt.off || tgt.removeEventListener ).apply( tgt, b.args );
|
||||
}
|
||||
|
||||
r.bindings = [];
|
||||
r.beforeRenderCallbacks = [];
|
||||
r.onUpdateEleCalcsFns = [];
|
||||
|
||||
if( r.removeObserver ){
|
||||
r.removeObserver.disconnect();
|
||||
}
|
||||
|
||||
if( r.styleObserver ){
|
||||
r.styleObserver.disconnect();
|
||||
}
|
||||
|
||||
if( r.resizeObserver ){
|
||||
r.resizeObserver.disconnect();
|
||||
}
|
||||
|
||||
if( r.labelCalcDiv ){
|
||||
try {
|
||||
document.body.removeChild( r.labelCalcDiv ); // eslint-disable-line no-undef
|
||||
} catch( e ){
|
||||
// ie10 issue #1014
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BRp.isHeadless = function(){
|
||||
return false;
|
||||
};
|
||||
|
||||
[
|
||||
arrowShapes,
|
||||
coordEleMath,
|
||||
images,
|
||||
loadListeners,
|
||||
nodeShapes,
|
||||
redraw
|
||||
].forEach( function( props ){
|
||||
util.extend( BRp, props );
|
||||
} );
|
||||
|
||||
export default BR;
|
||||
431
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-edges.mjs
generated
vendored
Normal file
431
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-edges.mjs
generated
vendored
Normal file
@@ -0,0 +1,431 @@
|
||||
/* global Path2D */
|
||||
|
||||
import * as util from '../../../util/index.mjs';
|
||||
import {drawPreparedRoundCorner} from "../../../round.mjs";
|
||||
|
||||
let CRp = {};
|
||||
|
||||
CRp.drawEdge = function( context, edge, shiftToOriginWithBb, drawLabel = true, shouldDrawOverlay = true, shouldDrawOpacity = true ){
|
||||
let r = this;
|
||||
let rs = edge._private.rscratch;
|
||||
|
||||
if( shouldDrawOpacity && !edge.visible() ){ return; }
|
||||
|
||||
// if bezier ctrl pts can not be calculated, then die
|
||||
if( rs.badLine || rs.allpts == null || isNaN(rs.allpts[0]) ){ // isNaN in case edge is impossible and browser bugs (e.g. safari)
|
||||
return;
|
||||
}
|
||||
|
||||
let bb;
|
||||
if( shiftToOriginWithBb ){
|
||||
bb = shiftToOriginWithBb;
|
||||
|
||||
context.translate( -bb.x1, -bb.y1 );
|
||||
}
|
||||
|
||||
let opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
|
||||
let lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
|
||||
|
||||
let curveStyle = edge.pstyle('curve-style').value;
|
||||
let lineStyle = edge.pstyle('line-style').value;
|
||||
let edgeWidth = edge.pstyle('width').pfValue;
|
||||
let lineCap = edge.pstyle('line-cap').value;
|
||||
let lineOutlineWidth = edge.pstyle('line-outline-width').value;
|
||||
let lineOutlineColor = edge.pstyle('line-outline-color').value;
|
||||
|
||||
let effectiveLineOpacity = opacity * lineOpacity;
|
||||
// separate arrow opacity would require arrow-opacity property
|
||||
let effectiveArrowOpacity = opacity * lineOpacity;
|
||||
|
||||
let drawLine = ( strokeOpacity = effectiveLineOpacity) => {
|
||||
if (curveStyle === 'straight-triangle') {
|
||||
r.eleStrokeStyle( context, edge, strokeOpacity );
|
||||
r.drawEdgeTrianglePath(
|
||||
edge,
|
||||
context,
|
||||
rs.allpts
|
||||
);
|
||||
} else {
|
||||
context.lineWidth = edgeWidth;
|
||||
context.lineCap = lineCap;
|
||||
|
||||
r.eleStrokeStyle( context, edge, strokeOpacity );
|
||||
r.drawEdgePath(
|
||||
edge,
|
||||
context,
|
||||
rs.allpts,
|
||||
lineStyle
|
||||
);
|
||||
|
||||
context.lineCap = 'butt'; // reset for other drawing functions
|
||||
}
|
||||
};
|
||||
|
||||
let drawLineOutline = ( strokeOpacity = effectiveLineOpacity) => {
|
||||
context.lineWidth = edgeWidth + lineOutlineWidth;
|
||||
context.lineCap = lineCap;
|
||||
|
||||
if (lineOutlineWidth > 0) {
|
||||
r.colorStrokeStyle( context, lineOutlineColor[0], lineOutlineColor[1], lineOutlineColor[2], strokeOpacity );
|
||||
} else {
|
||||
// do not draw any lineOutline
|
||||
context.lineCap = 'butt'; // reset for other drawing functions
|
||||
return;
|
||||
}
|
||||
|
||||
if (curveStyle === 'straight-triangle') {
|
||||
r.drawEdgeTrianglePath(
|
||||
edge,
|
||||
context,
|
||||
rs.allpts
|
||||
);
|
||||
} else {
|
||||
r.drawEdgePath(
|
||||
edge,
|
||||
context,
|
||||
rs.allpts,
|
||||
lineStyle
|
||||
);
|
||||
|
||||
context.lineCap = 'butt'; // reset for other drawing functions
|
||||
}
|
||||
};
|
||||
|
||||
let drawOverlay = () => {
|
||||
if( !shouldDrawOverlay ){ return; }
|
||||
|
||||
r.drawEdgeOverlay( context, edge );
|
||||
};
|
||||
|
||||
let drawUnderlay = () => {
|
||||
if( !shouldDrawOverlay ){ return; }
|
||||
|
||||
r.drawEdgeUnderlay( context, edge );
|
||||
};
|
||||
|
||||
let drawArrows = ( arrowOpacity = effectiveArrowOpacity) => {
|
||||
r.drawArrowheads( context, edge, arrowOpacity );
|
||||
};
|
||||
|
||||
let drawText = () => {
|
||||
r.drawElementText( context, edge, null, drawLabel );
|
||||
};
|
||||
|
||||
context.lineJoin = 'round';
|
||||
|
||||
let ghost = edge.pstyle('ghost').value === 'yes';
|
||||
|
||||
if( ghost ){
|
||||
let gx = edge.pstyle('ghost-offset-x').pfValue;
|
||||
let gy = edge.pstyle('ghost-offset-y').pfValue;
|
||||
let ghostOpacity = edge.pstyle('ghost-opacity').value;
|
||||
let effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
|
||||
|
||||
context.translate( gx, gy );
|
||||
|
||||
drawLine( effectiveGhostOpacity );
|
||||
drawArrows( effectiveGhostOpacity );
|
||||
|
||||
context.translate( -gx, -gy );
|
||||
} else {
|
||||
drawLineOutline();
|
||||
}
|
||||
|
||||
drawUnderlay();
|
||||
drawLine();
|
||||
drawArrows();
|
||||
drawOverlay();
|
||||
drawText();
|
||||
|
||||
if( shiftToOriginWithBb ){
|
||||
context.translate( bb.x1, bb.y1 );
|
||||
}
|
||||
};
|
||||
|
||||
const drawEdgeOverlayUnderlay = function( overlayOrUnderlay ) {
|
||||
if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
|
||||
throw new Error('Invalid state');
|
||||
}
|
||||
|
||||
return function( context, edge ){
|
||||
if( !edge.visible() ){ return; }
|
||||
|
||||
let opacity = edge.pstyle(`${overlayOrUnderlay}-opacity`).value;
|
||||
|
||||
if( opacity === 0 ){ return; }
|
||||
|
||||
let r = this;
|
||||
let usePaths = r.usePaths();
|
||||
let rs = edge._private.rscratch;
|
||||
|
||||
let padding = edge.pstyle(`${overlayOrUnderlay}-padding`).pfValue;
|
||||
let width = 2 * padding;
|
||||
let color = edge.pstyle(`${overlayOrUnderlay}-color`).value;
|
||||
|
||||
context.lineWidth = width;
|
||||
|
||||
if( rs.edgeType === 'self' && !usePaths ){
|
||||
context.lineCap = 'butt';
|
||||
} else {
|
||||
context.lineCap = 'round';
|
||||
}
|
||||
|
||||
r.colorStrokeStyle( context, color[0], color[1], color[2], opacity );
|
||||
|
||||
r.drawEdgePath(
|
||||
edge,
|
||||
context,
|
||||
rs.allpts,
|
||||
'solid'
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
CRp.drawEdgeOverlay = drawEdgeOverlayUnderlay('overlay');
|
||||
|
||||
CRp.drawEdgeUnderlay = drawEdgeOverlayUnderlay('underlay');
|
||||
|
||||
|
||||
CRp.drawEdgePath = function( edge, context, pts, type ){
|
||||
let rs = edge._private.rscratch;
|
||||
let canvasCxt = context;
|
||||
let path;
|
||||
let pathCacheHit = false;
|
||||
let usePaths = this.usePaths();
|
||||
let lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
|
||||
let lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
|
||||
|
||||
if( usePaths ){
|
||||
let pathCacheKey = pts.join( '$' );
|
||||
let keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
|
||||
|
||||
if( keyMatches ){
|
||||
path = context = rs.pathCache;
|
||||
pathCacheHit = true;
|
||||
} else {
|
||||
path = context = new Path2D();
|
||||
rs.pathCacheKey = pathCacheKey;
|
||||
rs.pathCache = path;
|
||||
}
|
||||
}
|
||||
|
||||
if( canvasCxt.setLineDash ){ // for very outofdate browsers
|
||||
switch( type ){
|
||||
case 'dotted':
|
||||
canvasCxt.setLineDash( [ 1, 1 ] );
|
||||
break;
|
||||
|
||||
case 'dashed':
|
||||
canvasCxt.setLineDash( lineDashPattern );
|
||||
canvasCxt.lineDashOffset = lineDashOffset;
|
||||
break;
|
||||
|
||||
case 'solid':
|
||||
canvasCxt.setLineDash( [ ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !pathCacheHit && !rs.badLine ){
|
||||
if( context.beginPath ){ context.beginPath(); }
|
||||
context.moveTo( pts[0], pts[1] );
|
||||
|
||||
switch( rs.edgeType ){
|
||||
case 'bezier':
|
||||
case 'self':
|
||||
case 'compound':
|
||||
case 'multibezier':
|
||||
for( let i = 2; i + 3 < pts.length; i += 4 ){
|
||||
context.quadraticCurveTo( pts[ i ], pts[ i + 1], pts[ i + 2], pts[ i + 3] );
|
||||
}
|
||||
break;
|
||||
|
||||
case 'straight':
|
||||
case 'haystack':
|
||||
for( let i = 2; i + 1 < pts.length; i += 2 ) {
|
||||
context.lineTo( pts[ i ], pts[ i + 1] );
|
||||
}
|
||||
break;
|
||||
case 'segments':
|
||||
if (rs.isRound) {
|
||||
for( let corner of rs.roundCorners ){
|
||||
drawPreparedRoundCorner(context, corner);
|
||||
}
|
||||
context.lineTo( pts[ pts.length - 2 ], pts[ pts.length - 1] );
|
||||
} else {
|
||||
for( let i = 2; i + 1 < pts.length; i += 2 ) {
|
||||
context.lineTo( pts[ i ], pts[ i + 1] );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
context = canvasCxt;
|
||||
if( usePaths ){
|
||||
context.stroke( path );
|
||||
} else {
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
// reset any line dashes
|
||||
if( context.setLineDash ){ // for very outofdate browsers
|
||||
context.setLineDash( [ ] );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
CRp.drawEdgeTrianglePath = function( edge, context, pts ){
|
||||
// use line stroke style for triangle fill style
|
||||
context.fillStyle = context.strokeStyle;
|
||||
|
||||
let edgeWidth = edge.pstyle('width').pfValue;
|
||||
|
||||
for( let i = 0; i + 1 < pts.length; i += 2 ){
|
||||
const vector = [ pts[ i + 2 ] - pts[ i ], pts[ i + 3 ] - pts[ i + 1 ] ];
|
||||
const length = Math.sqrt( vector[0] * vector[0] + vector[1] * vector[1] );
|
||||
const normal = [ vector[1] / length, -vector[0] / length ];
|
||||
const triangleHead = [ normal[0] * edgeWidth / 2, normal[1] * edgeWidth / 2 ];
|
||||
|
||||
context.beginPath();
|
||||
context.moveTo( pts[ i ] - triangleHead[0], pts[ i + 1 ] - triangleHead[1] );
|
||||
context.lineTo( pts[ i ] + triangleHead[0], pts[ i + 1 ] + triangleHead[1] );
|
||||
context.lineTo( pts[ i + 2 ], pts[ i + 3 ] );
|
||||
context.closePath();
|
||||
context.fill();
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawArrowheads = function( context, edge, opacity ){
|
||||
let rs = edge._private.rscratch;
|
||||
let isHaystack = rs.edgeType === 'haystack';
|
||||
|
||||
if( !isHaystack ){
|
||||
this.drawArrowhead( context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity );
|
||||
}
|
||||
|
||||
this.drawArrowhead( context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity );
|
||||
|
||||
this.drawArrowhead( context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity );
|
||||
|
||||
if( !isHaystack ){
|
||||
this.drawArrowhead( context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawArrowhead = function( context, edge, prefix, x, y, angle, opacity ){
|
||||
if( isNaN( x ) || x == null || isNaN( y ) || y == null || isNaN( angle ) || angle == null ){ return; }
|
||||
|
||||
let self = this;
|
||||
let arrowShape = edge.pstyle( prefix + '-arrow-shape' ).value;
|
||||
if( arrowShape === 'none' ) { return; }
|
||||
|
||||
let arrowClearFill = edge.pstyle( prefix + '-arrow-fill' ).value === 'hollow' ? 'both' : 'filled';
|
||||
let arrowFill = edge.pstyle( prefix + '-arrow-fill' ).value;
|
||||
let edgeWidth = edge.pstyle( 'width' ).pfValue;
|
||||
|
||||
let pArrowWidth = edge.pstyle( prefix + '-arrow-width' );
|
||||
let arrowWidth = pArrowWidth.value === 'match-line' ? edgeWidth : pArrowWidth.pfValue;
|
||||
if (pArrowWidth.units === '%') arrowWidth *= edgeWidth;
|
||||
|
||||
let edgeOpacity = edge.pstyle( 'opacity' ).value;
|
||||
|
||||
if( opacity === undefined ){
|
||||
opacity = edgeOpacity;
|
||||
}
|
||||
|
||||
let gco = context.globalCompositeOperation;
|
||||
|
||||
if( opacity !== 1 || arrowFill === 'hollow' ){ // then extra clear is needed
|
||||
context.globalCompositeOperation = 'destination-out';
|
||||
|
||||
self.colorFillStyle( context, 255, 255, 255, 1 );
|
||||
self.colorStrokeStyle( context, 255, 255, 255, 1 );
|
||||
|
||||
self.drawArrowShape( edge, context,
|
||||
arrowClearFill, edgeWidth, arrowShape, arrowWidth, x, y, angle
|
||||
);
|
||||
|
||||
context.globalCompositeOperation = gco;
|
||||
} // otherwise, the opaque arrow clears it for free :)
|
||||
|
||||
let color = edge.pstyle( prefix + '-arrow-color' ).value;
|
||||
self.colorFillStyle( context, color[0], color[1], color[2], opacity );
|
||||
self.colorStrokeStyle( context, color[0], color[1], color[2], opacity );
|
||||
|
||||
self.drawArrowShape( edge, context,
|
||||
arrowFill, edgeWidth, arrowShape, arrowWidth, x, y, angle
|
||||
);
|
||||
};
|
||||
|
||||
CRp.drawArrowShape = function( edge, context, fill, edgeWidth, shape, shapeWidth, x, y, angle ){
|
||||
let r = this;
|
||||
let usePaths = this.usePaths() && shape !== 'triangle-cross';
|
||||
let pathCacheHit = false;
|
||||
let path;
|
||||
let canvasContext = context;
|
||||
let translation = { x, y };
|
||||
let scale = edge.pstyle( 'arrow-scale' ).value;
|
||||
let size = this.getArrowWidth( edgeWidth, scale );
|
||||
let shapeImpl = r.arrowShapes[ shape ];
|
||||
|
||||
if( usePaths ){
|
||||
let cache = r.arrowPathCache = r.arrowPathCache || [];
|
||||
let key = util.hashString(shape);
|
||||
let cachedPath = cache[ key ];
|
||||
|
||||
if( cachedPath != null ){
|
||||
path = context = cachedPath;
|
||||
pathCacheHit = true;
|
||||
} else {
|
||||
path = context = new Path2D();
|
||||
cache[ key ] = path;
|
||||
}
|
||||
}
|
||||
|
||||
if( !pathCacheHit ){
|
||||
if( context.beginPath ){ context.beginPath(); }
|
||||
if( usePaths ){ // store in the path cache with values easily manipulated later
|
||||
shapeImpl.draw( context, 1, 0, { x: 0, y: 0 }, 1 );
|
||||
} else {
|
||||
shapeImpl.draw( context, size, angle, translation, edgeWidth );
|
||||
}
|
||||
if( context.closePath ){ context.closePath(); }
|
||||
}
|
||||
|
||||
context = canvasContext;
|
||||
|
||||
if( usePaths ){ // set transform to arrow position/orientation
|
||||
context.translate( x, y );
|
||||
context.rotate( angle );
|
||||
context.scale( size, size );
|
||||
}
|
||||
|
||||
if( fill === 'filled' || fill === 'both' ){
|
||||
if( usePaths ){
|
||||
context.fill( path );
|
||||
} else {
|
||||
context.fill();
|
||||
}
|
||||
}
|
||||
|
||||
if( fill === 'hollow' || fill === 'both' ){
|
||||
context.lineWidth = shapeWidth / (usePaths ? size : 1);
|
||||
context.lineJoin = 'miter';
|
||||
|
||||
if( usePaths ){
|
||||
context.stroke( path );
|
||||
} else {
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
if( usePaths ){ // reset transform by applying inverse
|
||||
context.scale( 1/size, 1/size );
|
||||
context.rotate( -angle );
|
||||
context.translate( -x, -y );
|
||||
}
|
||||
};
|
||||
|
||||
export default CRp;
|
||||
218
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-elements.mjs
generated
vendored
Normal file
218
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-elements.mjs
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
import * as math from '../../../math.mjs';
|
||||
|
||||
let CRp = {};
|
||||
|
||||
CRp.drawElement = function( context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity ){
|
||||
let r = this;
|
||||
|
||||
if( ele.isNode() ){
|
||||
r.drawNode( context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity );
|
||||
} else {
|
||||
r.drawEdge( context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawElementOverlay = function( context, ele ){
|
||||
let r = this;
|
||||
|
||||
if( ele.isNode() ){
|
||||
r.drawNodeOverlay( context, ele );
|
||||
} else {
|
||||
r.drawEdgeOverlay( context, ele );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawElementUnderlay = function( context, ele ){
|
||||
let r = this;
|
||||
|
||||
if( ele.isNode() ){
|
||||
r.drawNodeUnderlay( context, ele );
|
||||
} else {
|
||||
r.drawEdgeUnderlay( context, ele );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawCachedElementPortion = function( context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity ){
|
||||
let r = this;
|
||||
let bb = eleTxrCache.getBoundingBox(ele);
|
||||
|
||||
if( bb.w === 0 || bb.h === 0 ){ return; } // ignore zero size case
|
||||
|
||||
let eleCache = eleTxrCache.getElement( ele, bb, pxRatio, lvl, reason );
|
||||
|
||||
if( eleCache != null ){
|
||||
let opacity = getOpacity(r, ele);
|
||||
|
||||
if( opacity === 0 ){ return; }
|
||||
|
||||
let theta = getRotation(r, ele);
|
||||
let { x1, y1, w, h } = bb;
|
||||
let x, y, sx, sy, smooth;
|
||||
|
||||
if( theta !== 0 ){
|
||||
let rotPt = eleTxrCache.getRotationPoint(ele);
|
||||
|
||||
sx = rotPt.x;
|
||||
sy = rotPt.y;
|
||||
|
||||
context.translate(sx, sy);
|
||||
context.rotate(theta);
|
||||
|
||||
smooth = r.getImgSmoothing(context);
|
||||
|
||||
if( !smooth ){ r.setImgSmoothing(context, true); }
|
||||
|
||||
let off = eleTxrCache.getRotationOffset(ele);
|
||||
|
||||
x = off.x;
|
||||
y = off.y;
|
||||
} else {
|
||||
x = x1;
|
||||
y = y1;
|
||||
}
|
||||
|
||||
let oldGlobalAlpha;
|
||||
|
||||
if( opacity !== 1 ){
|
||||
oldGlobalAlpha = context.globalAlpha;
|
||||
context.globalAlpha = oldGlobalAlpha * opacity;
|
||||
}
|
||||
|
||||
context.drawImage( eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h );
|
||||
|
||||
if( opacity !== 1 ){
|
||||
context.globalAlpha = oldGlobalAlpha;
|
||||
}
|
||||
|
||||
if( theta !== 0 ){
|
||||
context.rotate(-theta);
|
||||
context.translate(-sx, -sy);
|
||||
|
||||
if( !smooth ){ r.setImgSmoothing(context, false); }
|
||||
}
|
||||
} else {
|
||||
eleTxrCache.drawElement( context, ele ); // direct draw fallback
|
||||
}
|
||||
};
|
||||
|
||||
const getZeroRotation = () => 0;
|
||||
const getLabelRotation = (r, ele) => r.getTextAngle(ele, null);
|
||||
const getSourceLabelRotation = (r, ele) => r.getTextAngle(ele, 'source');
|
||||
const getTargetLabelRotation = (r, ele) => r.getTextAngle(ele, 'target');
|
||||
const getOpacity = (r, ele) => ele.effectiveOpacity();
|
||||
const getTextOpacity = (e, ele) => ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
|
||||
|
||||
CRp.drawCachedElement = function( context, ele, pxRatio, extent, lvl, requestHighQuality ){
|
||||
let r = this;
|
||||
let { eleTxrCache, lblTxrCache, slbTxrCache, tlbTxrCache } = r.data;
|
||||
|
||||
let bb = ele.boundingBox();
|
||||
let reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
|
||||
|
||||
if( bb.w === 0 || bb.h === 0 || !ele.visible() ){ return; }
|
||||
|
||||
if( !extent || math.boundingBoxesIntersect( bb, extent ) ){
|
||||
let isEdge = ele.isEdge();
|
||||
let badLine = ele.element()._private.rscratch.badLine;
|
||||
|
||||
r.drawElementUnderlay( context, ele );
|
||||
|
||||
r.drawCachedElementPortion( context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity );
|
||||
|
||||
if( !isEdge || !badLine ){
|
||||
r.drawCachedElementPortion( context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity );
|
||||
}
|
||||
|
||||
if( isEdge && !badLine ){
|
||||
r.drawCachedElementPortion( context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity );
|
||||
r.drawCachedElementPortion( context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity );
|
||||
}
|
||||
|
||||
r.drawElementOverlay( context, ele );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawElements = function( context, eles ){
|
||||
let r = this;
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
|
||||
r.drawElement( context, ele );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawCachedElements = function( context, eles, pxRatio, extent ){
|
||||
let r = this;
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
|
||||
r.drawCachedElement( context, ele, pxRatio, extent );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawCachedNodes = function( context, eles, pxRatio, extent ){
|
||||
let r = this;
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[ i ];
|
||||
|
||||
if( !ele.isNode() ){ continue; }
|
||||
|
||||
r.drawCachedElement( context, ele, pxRatio, extent );
|
||||
}
|
||||
};
|
||||
|
||||
CRp.drawLayeredElements = function( context, eles, pxRatio, extent ){
|
||||
let r = this;
|
||||
|
||||
let layers = r.data.lyrTxrCache.getLayers( eles, pxRatio );
|
||||
|
||||
if( layers ){
|
||||
for( let i = 0; i < layers.length; i++ ){
|
||||
let layer = layers[i];
|
||||
let bb = layer.bb;
|
||||
|
||||
if( bb.w === 0 || bb.h === 0 ){ continue; }
|
||||
|
||||
context.drawImage( layer.canvas, bb.x1, bb.y1, bb.w, bb.h );
|
||||
}
|
||||
} else { // fall back on plain caching if no layers
|
||||
r.drawCachedElements( context, eles, pxRatio, extent );
|
||||
}
|
||||
};
|
||||
|
||||
if( process.env.NODE_ENV !== 'production' ){
|
||||
CRp.drawDebugPoints = function( context, eles ){
|
||||
let draw = function( x, y, color ){
|
||||
context.fillStyle = color;
|
||||
context.fillRect( x - 1, y - 1, 3, 3 );
|
||||
};
|
||||
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[i];
|
||||
let rs = ele._private.rscratch;
|
||||
|
||||
if( ele.isNode() ){
|
||||
let p = ele.position();
|
||||
|
||||
draw( rs.labelX, rs.labelY, 'red' );
|
||||
draw( p.x, p.y, 'magenta' );
|
||||
} else {
|
||||
let pts = rs.allpts;
|
||||
|
||||
for( let j = 0; j + 1 < pts.length; j += 2 ){
|
||||
let x = pts[ j ];
|
||||
let y = pts[ j + 1 ];
|
||||
|
||||
draw( x, y, 'cyan' );
|
||||
}
|
||||
|
||||
draw( rs.midX, rs.midY, 'yellow' );
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default CRp;
|
||||
804
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-nodes.mjs
generated
vendored
Normal file
804
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/drawing-nodes.mjs
generated
vendored
Normal file
@@ -0,0 +1,804 @@
|
||||
/* global Path2D */
|
||||
|
||||
import * as is from '../../../is.mjs';
|
||||
import {expandPolygon, joinLines} from '../../../math.mjs';
|
||||
import * as util from '../../../util/index.mjs';
|
||||
import * as round from "../../../round.mjs";
|
||||
import * as math from "../../../math.mjs";
|
||||
|
||||
let CRp = {};
|
||||
|
||||
CRp.drawNode = function( context, node, shiftToOriginWithBb, drawLabel = true, shouldDrawOverlay = true, shouldDrawOpacity = true ){
|
||||
let r = this;
|
||||
let nodeWidth, nodeHeight;
|
||||
let _p = node._private;
|
||||
let rs = _p.rscratch;
|
||||
let pos = node.position();
|
||||
|
||||
if( !is.number( pos.x ) || !is.number( pos.y ) ){
|
||||
return; // can't draw node with undefined position
|
||||
}
|
||||
|
||||
if( shouldDrawOpacity && !node.visible() ){ return; }
|
||||
|
||||
let eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
|
||||
|
||||
let usePaths = r.usePaths();
|
||||
let path;
|
||||
let pathCacheHit = false;
|
||||
|
||||
let padding = node.padding();
|
||||
|
||||
nodeWidth = node.width() + 2 * padding;
|
||||
nodeHeight = node.height() + 2 * padding;
|
||||
|
||||
//
|
||||
// setup shift
|
||||
|
||||
let bb;
|
||||
if( shiftToOriginWithBb ){
|
||||
bb = shiftToOriginWithBb;
|
||||
|
||||
context.translate( -bb.x1, -bb.y1 );
|
||||
}
|
||||
|
||||
//
|
||||
// load bg image
|
||||
|
||||
let bgImgProp = node.pstyle( 'background-image' );
|
||||
let urls = bgImgProp.value;
|
||||
let urlDefined = new Array( urls.length );
|
||||
let image = new Array( urls.length );
|
||||
let numImages = 0;
|
||||
for( let i = 0; i < urls.length; i++ ){
|
||||
let url = urls[i];
|
||||
let defd = urlDefined[i] = url != null && url !== 'none';
|
||||
|
||||
if( defd ){
|
||||
let bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
|
||||
|
||||
numImages++;
|
||||
|
||||
// get image, and if not loaded then ask to redraw when later loaded
|
||||
image[i] = r.getCachedImage( url, bgImgCrossOrigin, function(){
|
||||
_p.backgroundTimestamp = Date.now();
|
||||
|
||||
node.emitAndNotify('background');
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// setup styles
|
||||
|
||||
let darkness = node.pstyle('background-blacken').value;
|
||||
let borderWidth = node.pstyle('border-width').pfValue;
|
||||
let bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
|
||||
let borderColor = node.pstyle('border-color').value;
|
||||
let borderStyle = node.pstyle('border-style').value;
|
||||
let borderJoin = node.pstyle('border-join').value;
|
||||
let borderCap = node.pstyle('border-cap').value;
|
||||
let borderPosition = node.pstyle('border-position').value;
|
||||
let borderPattern = node.pstyle('border-dash-pattern').pfValue;
|
||||
let borderOffset = node.pstyle('border-dash-offset').pfValue;
|
||||
let borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
|
||||
let outlineWidth = node.pstyle('outline-width').pfValue;
|
||||
let outlineColor = node.pstyle('outline-color').value;
|
||||
let outlineStyle = node.pstyle('outline-style').value;
|
||||
let outlineOpacity = node.pstyle('outline-opacity').value * eleOpacity;
|
||||
let outlineOffset = node.pstyle('outline-offset').value;
|
||||
let cornerRadius = node.pstyle('corner-radius').value;
|
||||
if (cornerRadius !== 'auto') cornerRadius = node.pstyle('corner-radius').pfValue;
|
||||
|
||||
let setupShapeColor = ( bgOpy = bgOpacity ) => {
|
||||
r.eleFillStyle( context, node, bgOpy );
|
||||
};
|
||||
|
||||
let setupBorderColor = ( bdrOpy = borderOpacity ) => {
|
||||
r.colorStrokeStyle( context, borderColor[0], borderColor[1], borderColor[2], bdrOpy );
|
||||
};
|
||||
|
||||
let setupOutlineColor = ( otlnOpy = outlineOpacity ) => {
|
||||
r.colorStrokeStyle( context, outlineColor[0], outlineColor[1], outlineColor[2], otlnOpy );
|
||||
};
|
||||
|
||||
//
|
||||
// setup shape
|
||||
|
||||
let getPath = (width, height, shape, points) => {
|
||||
let pathCache = r.nodePathCache = r.nodePathCache || [];
|
||||
|
||||
let key = util.hashStrings(
|
||||
shape === 'polygon' ? shape + ',' + points.join(',') : shape,
|
||||
'' + height,
|
||||
'' + width,
|
||||
'' + cornerRadius
|
||||
);
|
||||
|
||||
let cachedPath = pathCache[ key ];
|
||||
let path;
|
||||
let cacheHit = false;
|
||||
|
||||
if( cachedPath != null ){
|
||||
path = cachedPath;
|
||||
cacheHit = true;
|
||||
rs.pathCache = path;
|
||||
} else {
|
||||
path = new Path2D();
|
||||
pathCache[ key ] = rs.pathCache = path;
|
||||
}
|
||||
|
||||
return {
|
||||
path,
|
||||
cacheHit
|
||||
};
|
||||
};
|
||||
|
||||
let styleShape = node.pstyle('shape').strValue;
|
||||
let shapePts = node.pstyle('shape-polygon-points').pfValue;
|
||||
|
||||
if( usePaths ){
|
||||
context.translate( pos.x, pos.y );
|
||||
|
||||
const shapePath = getPath(nodeWidth, nodeHeight, styleShape, shapePts);
|
||||
path = shapePath.path;
|
||||
pathCacheHit = shapePath.cacheHit;
|
||||
}
|
||||
|
||||
let drawShape = () => {
|
||||
if( !pathCacheHit ){
|
||||
|
||||
let npos = pos;
|
||||
|
||||
if( usePaths ){
|
||||
npos = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
}
|
||||
|
||||
r.nodeShapes[ r.getNodeShape( node ) ].draw(
|
||||
( path || context ),
|
||||
npos.x,
|
||||
npos.y,
|
||||
nodeWidth,
|
||||
nodeHeight, cornerRadius, rs );
|
||||
}
|
||||
|
||||
if( usePaths ){
|
||||
context.fill( path );
|
||||
} else {
|
||||
context.fill();
|
||||
}
|
||||
};
|
||||
|
||||
let drawImages = ( nodeOpacity = eleOpacity, inside = true ) => {
|
||||
let prevBging = _p.backgrounding;
|
||||
let totalCompleted = 0;
|
||||
|
||||
for( let i = 0; i < image.length; i++ ){
|
||||
const bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', i);
|
||||
if( inside && bgContainment === 'over' || !inside && bgContainment === 'inside' ){
|
||||
totalCompleted++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( urlDefined[i] && image[i].complete && !image[i].error ){
|
||||
totalCompleted++;
|
||||
r.drawInscribedImage( context, image[i], node, i, nodeOpacity );
|
||||
}
|
||||
}
|
||||
|
||||
_p.backgrounding = !(totalCompleted === numImages);
|
||||
if( prevBging !== _p.backgrounding ){ // update style b/c :backgrounding state changed
|
||||
node.updateStyle( false );
|
||||
}
|
||||
};
|
||||
|
||||
let drawPie = ( redrawShape = false, pieOpacity = eleOpacity ) => {
|
||||
if( r.hasPie( node ) ){
|
||||
r.drawPie( context, node, pieOpacity );
|
||||
|
||||
// redraw/restore path if steps after pie need it
|
||||
if( redrawShape ){
|
||||
|
||||
if( !usePaths ){
|
||||
r.nodeShapes[ r.getNodeShape( node ) ].draw(
|
||||
context,
|
||||
pos.x,
|
||||
pos.y,
|
||||
nodeWidth,
|
||||
nodeHeight, cornerRadius, rs );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let drawStripe = (redrawShape = false, stripeOpacity = eleOpacity) => {
|
||||
if( r.hasStripe( node ) ){
|
||||
context.save();
|
||||
|
||||
if (usePaths) {
|
||||
context.clip( rs.pathCache );
|
||||
} else {
|
||||
r.nodeShapes[ r.getNodeShape( node ) ].draw(
|
||||
context,
|
||||
pos.x,
|
||||
pos.y,
|
||||
nodeWidth,
|
||||
nodeHeight, cornerRadius, rs );
|
||||
context.clip();
|
||||
}
|
||||
|
||||
r.drawStripe( context, node, stripeOpacity );
|
||||
|
||||
context.restore();
|
||||
|
||||
// redraw/restore path if steps after stripes need it
|
||||
if( redrawShape ){
|
||||
|
||||
if( !usePaths ){
|
||||
r.nodeShapes[ r.getNodeShape( node ) ].draw(
|
||||
context,
|
||||
pos.x,
|
||||
pos.y,
|
||||
nodeWidth,
|
||||
nodeHeight, cornerRadius, rs );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let darken = ( darkenOpacity = eleOpacity ) => {
|
||||
let opacity = ( darkness > 0 ? darkness : -darkness ) * darkenOpacity;
|
||||
let c = darkness > 0 ? 0 : 255;
|
||||
|
||||
if( darkness !== 0 ){
|
||||
r.colorFillStyle( context, c, c, c, opacity );
|
||||
|
||||
if( usePaths ){
|
||||
context.fill( path );
|
||||
} else {
|
||||
context.fill();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let drawBorder = () => {
|
||||
if( borderWidth > 0 ){
|
||||
|
||||
context.lineWidth = borderWidth;
|
||||
context.lineCap = borderCap;
|
||||
context.lineJoin = borderJoin;
|
||||
|
||||
if( context.setLineDash ){ // for very outofdate browsers
|
||||
switch( borderStyle ){
|
||||
case 'dotted':
|
||||
context.setLineDash( [ 1, 1 ] );
|
||||
break;
|
||||
|
||||
case 'dashed':
|
||||
context.setLineDash( borderPattern );
|
||||
context.lineDashOffset = borderOffset;
|
||||
break;
|
||||
|
||||
case 'solid':
|
||||
case 'double':
|
||||
context.setLineDash( [ ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( borderPosition !== 'center') {
|
||||
context.save();
|
||||
context.lineWidth *= 2;
|
||||
if (borderPosition === 'inside') {
|
||||
usePaths ? context.clip(path) : context.clip();
|
||||
} else {
|
||||
const region = new Path2D();
|
||||
region.rect(
|
||||
-nodeWidth / 2 - borderWidth,
|
||||
-nodeHeight / 2 - borderWidth,
|
||||
nodeWidth + 2 * borderWidth,
|
||||
nodeHeight + 2 * borderWidth
|
||||
);
|
||||
region.addPath(path);
|
||||
context.clip(region, 'evenodd');
|
||||
}
|
||||
usePaths ? context.stroke(path) : context.stroke();
|
||||
context.restore();
|
||||
} else {
|
||||
usePaths ? context.stroke(path) : context.stroke();
|
||||
}
|
||||
|
||||
if( borderStyle === 'double' ){
|
||||
context.lineWidth = borderWidth / 3;
|
||||
|
||||
let gco = context.globalCompositeOperation;
|
||||
context.globalCompositeOperation = 'destination-out';
|
||||
|
||||
if( usePaths ){
|
||||
context.stroke( path );
|
||||
} else {
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
context.globalCompositeOperation = gco;
|
||||
}
|
||||
|
||||
// reset in case we changed the border style
|
||||
if( context.setLineDash ){ // for very outofdate browsers
|
||||
context.setLineDash( [ ] );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let drawOutline = () => {
|
||||
if( outlineWidth > 0 ){
|
||||
context.lineWidth = outlineWidth;
|
||||
context.lineCap = 'butt';
|
||||
|
||||
if( context.setLineDash ){ // for very outofdate browsers
|
||||
switch( outlineStyle ){
|
||||
case 'dotted':
|
||||
context.setLineDash( [ 1, 1 ] );
|
||||
break;
|
||||
|
||||
case 'dashed':
|
||||
context.setLineDash( [ 4, 2 ] );
|
||||
break;
|
||||
|
||||
case 'solid':
|
||||
case 'double':
|
||||
context.setLineDash( [ ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let npos = pos;
|
||||
|
||||
if( usePaths ){
|
||||
npos = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
}
|
||||
|
||||
let shape = r.getNodeShape( node );
|
||||
|
||||
let bWidth = borderWidth;
|
||||
if( borderPosition === 'inside' ) bWidth = 0;
|
||||
if( borderPosition === 'outside' ) bWidth *= 2;
|
||||
|
||||
let scaleX = (nodeWidth + bWidth + (outlineWidth + outlineOffset)) / nodeWidth;
|
||||
let scaleY = (nodeHeight + bWidth + (outlineWidth + outlineOffset)) / nodeHeight;
|
||||
let sWidth = nodeWidth * scaleX;
|
||||
let sHeight = nodeHeight * scaleY;
|
||||
|
||||
let points = r.nodeShapes[ shape ].points;
|
||||
let path;
|
||||
|
||||
if (usePaths) {
|
||||
let outlinePath = getPath(sWidth, sHeight, shape, points);
|
||||
path = outlinePath.path;
|
||||
}
|
||||
|
||||
// draw the outline path, either by using expanded points or by scaling
|
||||
// the dimensions, depending on shape
|
||||
if (shape === "ellipse") {
|
||||
r.drawEllipsePath(path || context, npos.x, npos.y, sWidth, sHeight);
|
||||
} else if ([
|
||||
'round-diamond', 'round-heptagon', 'round-hexagon', 'round-octagon',
|
||||
'round-pentagon', 'round-polygon', 'round-triangle', 'round-tag'
|
||||
].includes(shape)) {
|
||||
let sMult = 0;
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
||||
if (shape === 'round-diamond') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * 1.4;
|
||||
} else if (shape === 'round-heptagon') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * 1.075;
|
||||
offsetY = -(bWidth/2 + outlineOffset + outlineWidth) / 35;
|
||||
} else if (shape === 'round-hexagon') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
|
||||
} else if (shape === 'round-pentagon') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * 1.13;
|
||||
offsetY = -(bWidth/2 + outlineOffset + outlineWidth) / 15;
|
||||
} else if (shape === 'round-tag') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
|
||||
offsetX = (bWidth/2 + outlineWidth + outlineOffset) * .07;
|
||||
} else if (shape === 'round-triangle') {
|
||||
sMult = (bWidth + outlineOffset + outlineWidth) * (Math.PI/2);
|
||||
offsetY = -(bWidth + outlineOffset/2 + outlineWidth) / Math.PI;
|
||||
}
|
||||
|
||||
if (sMult !== 0) {
|
||||
scaleX = (nodeWidth + sMult)/nodeWidth;
|
||||
sWidth = nodeWidth * scaleX;
|
||||
if ( ! ['round-hexagon', 'round-tag'].includes(shape) ) {
|
||||
scaleY = (nodeHeight + sMult)/nodeHeight;
|
||||
sHeight = nodeHeight * scaleY;
|
||||
}
|
||||
}
|
||||
|
||||
cornerRadius = cornerRadius === 'auto' ? math.getRoundPolygonRadius( sWidth, sHeight ) : cornerRadius;
|
||||
|
||||
const halfW = sWidth / 2;
|
||||
const halfH = sHeight / 2;
|
||||
const radius = cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 2;
|
||||
const p = new Array( points.length / 2 );
|
||||
const corners = new Array( points.length / 2 );
|
||||
|
||||
for ( let i = 0; i < points.length / 2; i++ ){
|
||||
p[i] = {
|
||||
x: npos.x + offsetX + halfW * points[ i * 2 ],
|
||||
y: npos.y + offsetY + halfH * points[ i * 2 + 1 ]
|
||||
};
|
||||
}
|
||||
|
||||
let i, p1, p2, p3, len = p.length;
|
||||
|
||||
p1 = p[ len - 1 ];
|
||||
// for each point
|
||||
for( i = 0; i < len; i++ ){
|
||||
p2 = p[ (i) % len ];
|
||||
p3 = p[ (i + 1) % len ];
|
||||
corners[ i ] = round.getRoundCorner( p1, p2, p3, radius );
|
||||
p1 = p2;
|
||||
p2 = p3;
|
||||
}
|
||||
|
||||
r.drawRoundPolygonPath(path || context, npos.x + offsetX, npos.y + offsetY, nodeWidth * scaleX, nodeHeight * scaleY, points, corners );
|
||||
} else if (['roundrectangle', 'round-rectangle'].includes(shape)) {
|
||||
cornerRadius = cornerRadius === 'auto' ? math.getRoundRectangleRadius( sWidth, sHeight ) : cornerRadius;
|
||||
r.drawRoundRectanglePath(path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 2 );
|
||||
} else if (['cutrectangle', 'cut-rectangle'].includes(shape)) {
|
||||
cornerRadius = cornerRadius === 'auto' ? math.getCutRectangleCornerLength( sWidth, sHeight ) : cornerRadius;
|
||||
r.drawCutRectanglePath(path || context, npos.x, npos.y, sWidth, sHeight, null ,cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 4 );
|
||||
} else if (['bottomroundrectangle', 'bottom-round-rectangle'].includes(shape)) {
|
||||
cornerRadius = cornerRadius === 'auto' ? math.getRoundRectangleRadius( sWidth, sHeight ) : cornerRadius;
|
||||
r.drawBottomRoundRectanglePath(path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + ( bWidth + outlineWidth + outlineOffset ) / 2 );
|
||||
} else if (shape === "barrel") {
|
||||
r.drawBarrelPath(path || context, npos.x, npos.y, sWidth, sHeight);
|
||||
} else if (shape.startsWith("polygon") || ['rhomboid', 'right-rhomboid', 'round-tag', 'tag', 'vee'].includes(shape)) {
|
||||
let pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
|
||||
points = joinLines(expandPolygon(points, pad));
|
||||
r.drawPolygonPath(path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
|
||||
} else {
|
||||
let pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
|
||||
points = joinLines(expandPolygon(points, -pad));
|
||||
r.drawPolygonPath(path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
|
||||
}
|
||||
|
||||
if( usePaths ){
|
||||
context.stroke( path );
|
||||
} else {
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
if( outlineStyle === 'double' ){
|
||||
context.lineWidth = bWidth / 3;
|
||||
|
||||
let gco = context.globalCompositeOperation;
|
||||
context.globalCompositeOperation = 'destination-out';
|
||||
|
||||
if( usePaths ){
|
||||
context.stroke( path );
|
||||
} else {
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
context.globalCompositeOperation = gco;
|
||||
}
|
||||
|
||||
// reset in case we changed the border style
|
||||
if( context.setLineDash ){ // for very outofdate browsers
|
||||
context.setLineDash( [ ] );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let drawOverlay = () => {
|
||||
if( shouldDrawOverlay ){
|
||||
r.drawNodeOverlay( context, node, pos, nodeWidth, nodeHeight );
|
||||
}
|
||||
};
|
||||
|
||||
let drawUnderlay = () => {
|
||||
if( shouldDrawOverlay ){
|
||||
r.drawNodeUnderlay( context, node, pos, nodeWidth, nodeHeight );
|
||||
}
|
||||
};
|
||||
|
||||
let drawText = () => {
|
||||
r.drawElementText( context, node, null, drawLabel );
|
||||
};
|
||||
|
||||
let ghost = node.pstyle('ghost').value === 'yes';
|
||||
|
||||
if( ghost ){
|
||||
let gx = node.pstyle('ghost-offset-x').pfValue;
|
||||
let gy = node.pstyle('ghost-offset-y').pfValue;
|
||||
let ghostOpacity = node.pstyle('ghost-opacity').value;
|
||||
let effGhostOpacity = ghostOpacity * eleOpacity;
|
||||
|
||||
context.translate( gx, gy );
|
||||
|
||||
setupOutlineColor();
|
||||
drawOutline();
|
||||
setupShapeColor( ghostOpacity * bgOpacity );
|
||||
drawShape();
|
||||
drawImages( effGhostOpacity, true );
|
||||
setupBorderColor( ghostOpacity * borderOpacity );
|
||||
drawBorder();
|
||||
drawPie( darkness !== 0 || borderWidth !== 0 );
|
||||
drawStripe( darkness !== 0 || borderWidth !== 0 );
|
||||
drawImages( effGhostOpacity, false );
|
||||
darken( effGhostOpacity );
|
||||
|
||||
context.translate( -gx, -gy );
|
||||
}
|
||||
|
||||
if( usePaths ){
|
||||
context.translate( -pos.x, -pos.y );
|
||||
}
|
||||
drawUnderlay();
|
||||
if( usePaths ){
|
||||
context.translate( pos.x, pos.y );
|
||||
}
|
||||
|
||||
setupOutlineColor();
|
||||
drawOutline();
|
||||
setupShapeColor();
|
||||
drawShape();
|
||||
drawImages(eleOpacity, true);
|
||||
setupBorderColor();
|
||||
drawBorder();
|
||||
drawPie( darkness !== 0 || borderWidth !== 0 );
|
||||
drawStripe( darkness !== 0 || borderWidth !== 0 );
|
||||
drawImages(eleOpacity, false);
|
||||
|
||||
darken();
|
||||
|
||||
if( usePaths ){
|
||||
context.translate( -pos.x, -pos.y );
|
||||
}
|
||||
|
||||
drawText();
|
||||
|
||||
drawOverlay();
|
||||
|
||||
//
|
||||
// clean up shift
|
||||
|
||||
if( shiftToOriginWithBb ){
|
||||
context.translate( bb.x1, bb.y1 );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const drawNodeOverlayUnderlay = function( overlayOrUnderlay ) {
|
||||
if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
|
||||
throw new Error('Invalid state');
|
||||
}
|
||||
|
||||
return function( context, node, pos, nodeWidth, nodeHeight ){
|
||||
let r = this;
|
||||
|
||||
if( !node.visible() ){ return; }
|
||||
|
||||
let padding = node.pstyle( `${overlayOrUnderlay}-padding` ).pfValue;
|
||||
let opacity = node.pstyle( `${overlayOrUnderlay}-opacity` ).value;
|
||||
let color = node.pstyle( `${overlayOrUnderlay}-color` ).value;
|
||||
let shape = node.pstyle( `${overlayOrUnderlay}-shape` ).value;
|
||||
let radius = node.pstyle( `${overlayOrUnderlay}-corner-radius` ).value;
|
||||
|
||||
if( opacity > 0 ){
|
||||
pos = pos || node.position();
|
||||
|
||||
if( nodeWidth == null || nodeHeight == null ){
|
||||
let padding = node.padding();
|
||||
|
||||
nodeWidth = node.width() + 2 * padding;
|
||||
nodeHeight = node.height() + 2 * padding;
|
||||
}
|
||||
|
||||
r.colorFillStyle( context, color[0], color[1], color[2], opacity );
|
||||
|
||||
r.nodeShapes[shape].draw(
|
||||
context,
|
||||
pos.x,
|
||||
pos.y,
|
||||
nodeWidth + padding * 2,
|
||||
nodeHeight + padding * 2,
|
||||
radius
|
||||
);
|
||||
|
||||
context.fill();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
CRp.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
|
||||
|
||||
CRp.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay');
|
||||
|
||||
// does the node have at least one pie piece?
|
||||
CRp.hasPie = function( node ){
|
||||
node = node[0]; // ensure ele ref
|
||||
|
||||
return node._private.hasPie;
|
||||
};
|
||||
|
||||
CRp.hasStripe = function( node ){
|
||||
node = node[0]; // ensure ele ref
|
||||
|
||||
return node._private.hasStripe;
|
||||
};
|
||||
|
||||
CRp.drawPie = function( context, node, nodeOpacity, pos ){
|
||||
node = node[0]; // ensure ele ref
|
||||
pos = pos || node.position();
|
||||
|
||||
let cyStyle = node.cy().style();
|
||||
let pieSize = node.pstyle( 'pie-size' );
|
||||
let hole = node.pstyle('pie-hole');
|
||||
let overallStartAngle = node.pstyle('pie-start-angle').pfValue;
|
||||
let x = pos.x;
|
||||
let y = pos.y;
|
||||
let nodeW = node.width();
|
||||
let nodeH = node.height();
|
||||
let radius = Math.min( nodeW, nodeH ) / 2; // must fit in node
|
||||
let holeRadius;
|
||||
let lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
|
||||
let usePaths = this.usePaths();
|
||||
|
||||
if( usePaths ){
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
if( pieSize.units === '%' ){
|
||||
radius = radius * pieSize.pfValue;
|
||||
} else if( pieSize.pfValue !== undefined ){
|
||||
radius = pieSize.pfValue / 2; // diameter in pixels => radius
|
||||
}
|
||||
|
||||
if (hole.units === '%') {
|
||||
holeRadius = radius * hole.pfValue;
|
||||
} else if (hole.pfValue !== undefined) {
|
||||
holeRadius = hole.pfValue / 2; // diameter in pixels => radius
|
||||
}
|
||||
|
||||
if (holeRadius >= radius) {
|
||||
return; // the pie would be invisible anyway
|
||||
}
|
||||
|
||||
for( let i = 1; i <= cyStyle.pieBackgroundN; i++ ){ // 1..N
|
||||
let size = node.pstyle( 'pie-' + i + '-background-size' ).value;
|
||||
let color = node.pstyle( 'pie-' + i + '-background-color' ).value;
|
||||
let opacity = node.pstyle( 'pie-' + i + '-background-opacity' ).value * nodeOpacity;
|
||||
let percent = size / 100; // map integer range [0, 100] to [0, 1]
|
||||
|
||||
// percent can't push beyond 1
|
||||
if( percent + lastPercent > 1 ){
|
||||
percent = 1 - lastPercent;
|
||||
}
|
||||
|
||||
let angleStart = (1.5 * Math.PI) + (2 * Math.PI * lastPercent); // start at 12 o'clock and go clockwise
|
||||
angleStart += overallStartAngle; // shift by the overall pie start angle
|
||||
let angleDelta = 2 * Math.PI * percent;
|
||||
let angleEnd = angleStart + angleDelta;
|
||||
|
||||
// ignore if
|
||||
// - zero size
|
||||
// - we're already beyond the full circle
|
||||
// - adding the current slice would go beyond the full circle
|
||||
if( size === 0 || lastPercent >= 1 || lastPercent + percent > 1 ){
|
||||
continue;
|
||||
}
|
||||
|
||||
if (holeRadius === 0) { // make a pie slice
|
||||
context.beginPath();
|
||||
context.moveTo( x, y );
|
||||
context.arc( x, y, radius, angleStart, angleEnd );
|
||||
context.closePath();
|
||||
} else { // make a pie slice that's like the above but with a hole in the middle
|
||||
context.beginPath();
|
||||
context.arc(x, y, radius, angleStart, angleEnd);
|
||||
context.arc(x, y, holeRadius, angleEnd, angleStart, true); // true for anticlockwise
|
||||
context.closePath();
|
||||
}
|
||||
|
||||
this.colorFillStyle( context, color[0], color[1], color[2], opacity );
|
||||
|
||||
context.fill();
|
||||
|
||||
lastPercent += percent;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CRp.drawStripe = function( context, node, nodeOpacity, pos ){
|
||||
node = node[0]; // ensure ele ref
|
||||
pos = pos || node.position();
|
||||
|
||||
let cyStyle = node.cy().style();
|
||||
let x = pos.x;
|
||||
let y = pos.y;
|
||||
let nodeW = node.width();
|
||||
let nodeH = node.height();
|
||||
let lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
|
||||
let usePaths = this.usePaths();
|
||||
|
||||
context.save();
|
||||
|
||||
let direction = node.pstyle('stripe-direction').value;
|
||||
let stripeSize = node.pstyle('stripe-size');
|
||||
|
||||
switch (direction) {
|
||||
case 'vertical':
|
||||
break; // default
|
||||
case 'righward':
|
||||
context.rotate(-Math.PI / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
let stripeW = nodeW;
|
||||
let stripeH = nodeH;
|
||||
|
||||
if( stripeSize.units === '%' ){
|
||||
stripeW = stripeW * stripeSize.pfValue;
|
||||
stripeH = stripeH * stripeSize.pfValue;
|
||||
} else if( stripeSize.pfValue !== undefined ){
|
||||
stripeW = stripeSize.pfValue;
|
||||
stripeH = stripeSize.pfValue;
|
||||
}
|
||||
|
||||
if( usePaths ){
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
// shift up from the centre of the node to the top-left corner
|
||||
y -= stripeW / 2;
|
||||
x -= stripeH / 2;
|
||||
|
||||
for( let i = 1; i <= cyStyle.stripeBackgroundN; i++ ){ // 1..N
|
||||
let size = node.pstyle( 'stripe-' + i + '-background-size' ).value;
|
||||
let color = node.pstyle( 'stripe-' + i + '-background-color' ).value;
|
||||
let opacity = node.pstyle( 'stripe-' + i + '-background-opacity' ).value * nodeOpacity;
|
||||
let percent = size / 100; // map integer range [0, 100] to [0, 1]
|
||||
|
||||
// percent can't push beyond 1
|
||||
if( percent + lastPercent > 1 ){
|
||||
percent = 1 - lastPercent;
|
||||
}
|
||||
|
||||
// ignore if
|
||||
// - zero size
|
||||
// - we're already beyond the full chart
|
||||
// - adding the current slice would go beyond the full chart
|
||||
if( size === 0 || lastPercent >= 1 || lastPercent + percent > 1 ){
|
||||
continue;
|
||||
}
|
||||
|
||||
// draw rect for the current stripe
|
||||
context.beginPath();
|
||||
context.rect( x, y + stripeH * lastPercent, stripeW, stripeH * percent );
|
||||
context.closePath();
|
||||
|
||||
this.colorFillStyle( context, color[0], color[1], color[2], opacity );
|
||||
|
||||
context.fill();
|
||||
|
||||
lastPercent += percent;
|
||||
}
|
||||
|
||||
context.restore();
|
||||
|
||||
};
|
||||
|
||||
|
||||
export default CRp;
|
||||
555
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/ele-texture-cache.mjs
generated
vendored
Normal file
555
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/ele-texture-cache.mjs
generated
vendored
Normal file
@@ -0,0 +1,555 @@
|
||||
import * as math from '../../../math.mjs';
|
||||
import { trueify, falsify, removeFromArray, clearArray, MAX_INT, assign, defaults } from '../../../util/index.mjs';
|
||||
import Heap from '../../../heap.mjs';
|
||||
import defs from './texture-cache-defs.mjs';
|
||||
import ElementTextureCacheLookup from './ele-texture-cache-lookup.mjs';
|
||||
|
||||
const minTxrH = 25; // the size of the texture cache for small height eles (special case)
|
||||
const txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
|
||||
const minLvl = -4; // when scaling smaller than that we don't need to re-render
|
||||
export const maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
|
||||
export const maxZoom = 7.99; // beyond this zoom level, layered textures are not used
|
||||
const eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
|
||||
const defTxrWidth = 1024; // default/minimum texture width
|
||||
const maxTxrW = 1024; // the maximum width of a texture
|
||||
const maxTxrH = 1024; // the maximum height of a texture
|
||||
const minUtility = 0.2; // if usage of texture is less than this, it is retired
|
||||
const maxFullness = 0.8; // fullness of texture after which queue removal is checked
|
||||
const maxFullnessChecks = 10; // dequeued after this many checks
|
||||
const deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
|
||||
const deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
|
||||
const deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
|
||||
const deqFastCost = 0.9; // % of frame time to be used when >60fps
|
||||
const deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
|
||||
const maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
|
||||
|
||||
const getTxrReasons = {
|
||||
dequeue: 'dequeue',
|
||||
downscale: 'downscale',
|
||||
highQuality: 'highQuality'
|
||||
};
|
||||
|
||||
const initDefaults = defaults({
|
||||
getKey: null,
|
||||
doesEleInvalidateKey: falsify,
|
||||
drawElement: null,
|
||||
getBoundingBox: null,
|
||||
getRotationPoint: null,
|
||||
getRotationOffset: null,
|
||||
isVisible: trueify,
|
||||
allowEdgeTxrCaching: true,
|
||||
allowParentTxrCaching: true
|
||||
});
|
||||
|
||||
const ElementTextureCache = function( renderer, initOptions ){
|
||||
let self = this;
|
||||
|
||||
self.renderer = renderer;
|
||||
self.onDequeues = [];
|
||||
|
||||
let opts = initDefaults(initOptions);
|
||||
|
||||
assign(self, opts);
|
||||
|
||||
self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
|
||||
|
||||
self.setupDequeueing();
|
||||
};
|
||||
|
||||
const ETCp = ElementTextureCache.prototype;
|
||||
|
||||
ETCp.reasons = getTxrReasons;
|
||||
|
||||
// the list of textures in which new subtextures for elements can be placed
|
||||
ETCp.getTextureQueue = function( txrH ){
|
||||
let self = this;
|
||||
self.eleImgCaches = self.eleImgCaches || {};
|
||||
|
||||
return ( self.eleImgCaches[ txrH ] = self.eleImgCaches[ txrH ] || [] );
|
||||
};
|
||||
|
||||
// the list of usused textures which can be recycled (in use in texture queue)
|
||||
ETCp.getRetiredTextureQueue = function( txrH ){
|
||||
let self = this;
|
||||
|
||||
let rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
|
||||
let rtxtrQ = rtxtrQs[ txrH ] = rtxtrQs[ txrH ] || [];
|
||||
|
||||
return rtxtrQ;
|
||||
};
|
||||
|
||||
// queue of element draw requests at different scale levels
|
||||
ETCp.getElementQueue = function(){
|
||||
let self = this;
|
||||
|
||||
let q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function( a, b ){
|
||||
return b.reqs - a.reqs;
|
||||
});
|
||||
|
||||
return q;
|
||||
};
|
||||
|
||||
// queue of element draw requests at different scale levels (element id lookup)
|
||||
ETCp.getElementKeyToQueue = function(){
|
||||
let self = this;
|
||||
|
||||
let k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
|
||||
|
||||
return k2q;
|
||||
};
|
||||
|
||||
ETCp.getElement = function( ele, bb, pxRatio, lvl, reason ){
|
||||
let self = this;
|
||||
let r = this.renderer;
|
||||
let zoom = r.cy.zoom();
|
||||
let lookup = this.lookup;
|
||||
|
||||
if( !bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed() ){ return null; }
|
||||
|
||||
if(
|
||||
( !self.allowEdgeTxrCaching && ele.isEdge() )
|
||||
|| ( !self.allowParentTxrCaching && ele.isParent() )
|
||||
){
|
||||
return null;
|
||||
}
|
||||
|
||||
if( lvl == null ){
|
||||
lvl = Math.ceil( math.log2( zoom * pxRatio ) );
|
||||
}
|
||||
|
||||
if( lvl < minLvl ){
|
||||
lvl = minLvl;
|
||||
} else if( zoom >= maxZoom || lvl > maxLvl ){
|
||||
return null;
|
||||
}
|
||||
|
||||
let scale = Math.pow( 2, lvl );
|
||||
let eleScaledH = bb.h * scale;
|
||||
let eleScaledW = bb.w * scale;
|
||||
let scaledLabelShown = r.eleTextBiggerThanMin( ele, scale );
|
||||
|
||||
if( !this.isVisible(ele, scaledLabelShown) ){ return null; }
|
||||
|
||||
let eleCache = lookup.get( ele, lvl );
|
||||
|
||||
// if this get was on an unused/invalidated cache, then restore the texture usage metric
|
||||
if( eleCache && eleCache.invalidated ){
|
||||
eleCache.invalidated = false;
|
||||
eleCache.texture.invalidatedWidth -= eleCache.width;
|
||||
}
|
||||
|
||||
if( eleCache ){
|
||||
return eleCache;
|
||||
}
|
||||
|
||||
let txrH; // which texture height this ele belongs to
|
||||
|
||||
if( eleScaledH <= minTxrH ){
|
||||
txrH = minTxrH;
|
||||
} else if( eleScaledH <= txrStepH ){
|
||||
txrH = txrStepH;
|
||||
} else {
|
||||
txrH = Math.ceil( eleScaledH / txrStepH ) * txrStepH;
|
||||
}
|
||||
|
||||
if( eleScaledH > maxTxrH || eleScaledW > maxTxrW ){
|
||||
return null; // caching large elements is not efficient
|
||||
}
|
||||
|
||||
let txrQ = self.getTextureQueue( txrH );
|
||||
|
||||
// first try the second last one in case it has space at the end
|
||||
let txr = txrQ[ txrQ.length - 2 ];
|
||||
|
||||
let addNewTxr = function(){
|
||||
return self.recycleTexture( txrH, eleScaledW ) || self.addTexture( txrH, eleScaledW );
|
||||
};
|
||||
|
||||
// try the last one if there is no second last one
|
||||
if( !txr ){
|
||||
txr = txrQ[ txrQ.length - 1 ];
|
||||
}
|
||||
|
||||
// if the last one doesn't exist, we need a first one
|
||||
if( !txr ){
|
||||
txr = addNewTxr();
|
||||
}
|
||||
|
||||
// if there's no room in the current texture, we need a new one
|
||||
if( txr.width - txr.usedWidth < eleScaledW ){
|
||||
txr = addNewTxr();
|
||||
}
|
||||
|
||||
let scalableFrom = function( otherCache ){
|
||||
return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
|
||||
};
|
||||
|
||||
let deqing = reason && reason === getTxrReasons.dequeue;
|
||||
let highQualityReq = reason && reason === getTxrReasons.highQuality;
|
||||
let downscaleReq = reason && reason === getTxrReasons.downscale;
|
||||
|
||||
let higherCache; // the nearest cache with a higher level
|
||||
for( let l = lvl + 1; l <= maxLvl; l++ ){
|
||||
let c = lookup.get( ele, l );
|
||||
|
||||
if( c ){ higherCache = c; break; }
|
||||
}
|
||||
|
||||
let oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
|
||||
|
||||
let downscale = function(){
|
||||
txr.context.drawImage(
|
||||
oneUpCache.texture.canvas,
|
||||
oneUpCache.x, 0,
|
||||
oneUpCache.width, oneUpCache.height,
|
||||
txr.usedWidth, 0,
|
||||
eleScaledW, eleScaledH
|
||||
);
|
||||
};
|
||||
|
||||
// reset ele area in texture
|
||||
txr.context.setTransform( 1, 0, 0, 1, 0, 0 );
|
||||
txr.context.clearRect( txr.usedWidth, 0, eleScaledW, txrH );
|
||||
|
||||
if( scalableFrom(oneUpCache) ){
|
||||
// then we can relatively cheaply rescale the existing image w/o rerendering
|
||||
downscale();
|
||||
|
||||
} else if( scalableFrom(higherCache) ){
|
||||
// then use the higher cache for now and queue the next level down
|
||||
// to cheaply scale towards the smaller level
|
||||
|
||||
if( highQualityReq ){
|
||||
for( let l = higherCache.level; l > lvl; l-- ){
|
||||
oneUpCache = self.getElement( ele, bb, pxRatio, l, getTxrReasons.downscale );
|
||||
}
|
||||
|
||||
downscale();
|
||||
|
||||
} else {
|
||||
self.queueElement( ele, higherCache.level - 1 );
|
||||
|
||||
return higherCache;
|
||||
}
|
||||
} else {
|
||||
|
||||
let lowerCache; // the nearest cache with a lower level
|
||||
if( !deqing && !highQualityReq && !downscaleReq ){
|
||||
for( let l = lvl - 1; l >= minLvl; l-- ){
|
||||
let c = lookup.get( ele, l );
|
||||
|
||||
if( c ){ lowerCache = c; break; }
|
||||
}
|
||||
}
|
||||
|
||||
if( scalableFrom(lowerCache) ){
|
||||
// then use the lower quality cache for now and queue the better one for later
|
||||
|
||||
self.queueElement( ele, lvl );
|
||||
|
||||
return lowerCache;
|
||||
}
|
||||
|
||||
txr.context.translate( txr.usedWidth, 0 );
|
||||
txr.context.scale( scale, scale );
|
||||
|
||||
this.drawElement( txr.context, ele, bb, scaledLabelShown, false );
|
||||
|
||||
txr.context.scale( 1/scale, 1/scale );
|
||||
txr.context.translate( -txr.usedWidth, 0 );
|
||||
}
|
||||
|
||||
eleCache = {
|
||||
x: txr.usedWidth,
|
||||
texture: txr,
|
||||
level: lvl,
|
||||
scale: scale,
|
||||
width: eleScaledW,
|
||||
height: eleScaledH,
|
||||
scaledLabelShown: scaledLabelShown
|
||||
};
|
||||
|
||||
txr.usedWidth += Math.ceil( eleScaledW + eleTxrSpacing );
|
||||
|
||||
txr.eleCaches.push( eleCache );
|
||||
|
||||
lookup.set( ele, lvl, eleCache );
|
||||
|
||||
self.checkTextureFullness( txr );
|
||||
|
||||
return eleCache;
|
||||
};
|
||||
|
||||
ETCp.invalidateElements = function( eles ){
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
this.invalidateElement(eles[i]);
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.invalidateElement = function( ele ){
|
||||
let self = this;
|
||||
let lookup = self.lookup;
|
||||
let caches = [];
|
||||
let invalid = lookup.isInvalid(ele);
|
||||
|
||||
if( !invalid ){
|
||||
return; // override the invalidation request if the element key has not changed
|
||||
}
|
||||
|
||||
for( let lvl = minLvl; lvl <= maxLvl; lvl++ ){
|
||||
let cache = lookup.getForCachedKey( ele, lvl );
|
||||
|
||||
if( cache ){
|
||||
caches.push( cache );
|
||||
}
|
||||
}
|
||||
|
||||
let noOtherElesUseCache = lookup.invalidate(ele);
|
||||
|
||||
if( noOtherElesUseCache ){
|
||||
for( let i = 0; i < caches.length; i++ ){
|
||||
let cache = caches[i];
|
||||
let txr = cache.texture;
|
||||
|
||||
// remove space from the texture it belongs to
|
||||
txr.invalidatedWidth += cache.width;
|
||||
|
||||
// mark the cache as invalidated
|
||||
cache.invalidated = true;
|
||||
|
||||
// retire the texture if its utility is low
|
||||
self.checkTextureUtility( txr );
|
||||
}
|
||||
}
|
||||
|
||||
// remove from queue since the old req was for the old state
|
||||
self.removeFromQueue( ele );
|
||||
};
|
||||
|
||||
ETCp.checkTextureUtility = function( txr ){
|
||||
// invalidate all entries in the cache if the cache size is small
|
||||
if( txr.invalidatedWidth >= minUtility * txr.width ){
|
||||
this.retireTexture( txr );
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.checkTextureFullness = function( txr ){
|
||||
// if texture has been mostly filled and passed over several times, remove
|
||||
// it from the queue so we don't need to waste time looking at it to put new things
|
||||
|
||||
let self = this;
|
||||
let txrQ = self.getTextureQueue( txr.height );
|
||||
|
||||
if( txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks ){
|
||||
removeFromArray( txrQ, txr );
|
||||
} else {
|
||||
txr.fullnessChecks++;
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.retireTexture = function( txr ){
|
||||
let self = this;
|
||||
let txrH = txr.height;
|
||||
let txrQ = self.getTextureQueue( txrH );
|
||||
let lookup = this.lookup;
|
||||
|
||||
// retire the texture from the active / searchable queue:
|
||||
|
||||
removeFromArray( txrQ, txr );
|
||||
|
||||
txr.retired = true;
|
||||
|
||||
// remove the refs from the eles to the caches:
|
||||
|
||||
let eleCaches = txr.eleCaches;
|
||||
|
||||
for( let i = 0; i < eleCaches.length; i++ ){
|
||||
let eleCache = eleCaches[i];
|
||||
|
||||
lookup.deleteCache( eleCache.key, eleCache.level );
|
||||
}
|
||||
|
||||
clearArray( eleCaches );
|
||||
|
||||
// add the texture to a retired queue so it can be recycled in future:
|
||||
|
||||
let rtxtrQ = self.getRetiredTextureQueue( txrH );
|
||||
|
||||
rtxtrQ.push( txr );
|
||||
};
|
||||
|
||||
ETCp.addTexture = function( txrH, minW ){
|
||||
let self = this;
|
||||
let txrQ = self.getTextureQueue( txrH );
|
||||
let txr = {};
|
||||
|
||||
txrQ.push( txr );
|
||||
|
||||
txr.eleCaches = [];
|
||||
|
||||
txr.height = txrH;
|
||||
txr.width = Math.max( defTxrWidth, minW );
|
||||
txr.usedWidth = 0;
|
||||
txr.invalidatedWidth = 0;
|
||||
txr.fullnessChecks = 0;
|
||||
|
||||
txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
|
||||
|
||||
txr.context = txr.canvas.getContext('2d');
|
||||
|
||||
return txr;
|
||||
};
|
||||
|
||||
ETCp.recycleTexture = function( txrH, minW ){
|
||||
let self = this;
|
||||
let txrQ = self.getTextureQueue( txrH );
|
||||
let rtxtrQ = self.getRetiredTextureQueue( txrH );
|
||||
|
||||
for( let i = 0; i < rtxtrQ.length; i++ ){
|
||||
let txr = rtxtrQ[i];
|
||||
|
||||
if( txr.width >= minW ){
|
||||
txr.retired = false;
|
||||
|
||||
txr.usedWidth = 0;
|
||||
txr.invalidatedWidth = 0;
|
||||
txr.fullnessChecks = 0;
|
||||
|
||||
clearArray( txr.eleCaches );
|
||||
|
||||
txr.context.setTransform( 1, 0, 0, 1, 0, 0 );
|
||||
txr.context.clearRect( 0, 0, txr.width, txr.height );
|
||||
|
||||
removeFromArray( rtxtrQ, txr );
|
||||
txrQ.push( txr );
|
||||
|
||||
return txr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.queueElement = function( ele, lvl ){
|
||||
let self = this;
|
||||
let q = self.getElementQueue();
|
||||
let k2q = self.getElementKeyToQueue();
|
||||
let key = this.getKey(ele);
|
||||
let existingReq = k2q[key];
|
||||
|
||||
if( existingReq ){
|
||||
// use the max lvl b/c in between lvls are cheap to make
|
||||
existingReq.level = Math.max( existingReq.level, lvl );
|
||||
|
||||
existingReq.eles.merge(ele);
|
||||
|
||||
existingReq.reqs++;
|
||||
|
||||
q.updateItem( existingReq );
|
||||
} else {
|
||||
let req = {
|
||||
eles: ele.spawn().merge(ele),
|
||||
level: lvl,
|
||||
reqs: 1,
|
||||
key
|
||||
};
|
||||
|
||||
q.push( req );
|
||||
|
||||
k2q[key] = req;
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.dequeue = function( pxRatio /*, extent*/ ){
|
||||
let self = this;
|
||||
let q = self.getElementQueue();
|
||||
let k2q = self.getElementKeyToQueue();
|
||||
let dequeued = [];
|
||||
let lookup = self.lookup;
|
||||
|
||||
for( let i = 0; i < maxDeqSize; i++ ){
|
||||
if( q.size() > 0 ){
|
||||
let req = q.pop();
|
||||
let key = req.key;
|
||||
let ele = req.eles[0]; // all eles have the same key
|
||||
let cacheExists = lookup.hasCache(ele, req.level);
|
||||
|
||||
// clear out the key to req lookup
|
||||
k2q[key] = null;
|
||||
|
||||
// dequeueing isn't necessary with an existing cache
|
||||
if( cacheExists ){ continue; }
|
||||
|
||||
dequeued.push( req );
|
||||
|
||||
let bb = self.getBoundingBox( ele );
|
||||
|
||||
self.getElement( ele, bb, pxRatio, req.level, getTxrReasons.dequeue );
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dequeued;
|
||||
};
|
||||
|
||||
ETCp.removeFromQueue = function( ele ){
|
||||
let self = this;
|
||||
let q = self.getElementQueue();
|
||||
let k2q = self.getElementKeyToQueue();
|
||||
let key = this.getKey(ele);
|
||||
let req = k2q[key];
|
||||
|
||||
if( req != null ){
|
||||
if( req.eles.length === 1 ){ // remove if last ele in the req
|
||||
// bring to front of queue
|
||||
req.reqs = MAX_INT;
|
||||
q.updateItem(req);
|
||||
|
||||
q.pop(); // remove from queue
|
||||
|
||||
k2q[key] = null; // remove from lookup map
|
||||
} else { // otherwise just remove ele from req
|
||||
req.eles.unmerge(ele);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ETCp.onDequeue = function( fn ){ this.onDequeues.push( fn ); };
|
||||
ETCp.offDequeue = function( fn ){ removeFromArray( this.onDequeues, fn ); };
|
||||
|
||||
ETCp.setupDequeueing = defs.setupDequeueing({
|
||||
deqRedrawThreshold: deqRedrawThreshold,
|
||||
deqCost: deqCost,
|
||||
deqAvgCost: deqAvgCost,
|
||||
deqNoDrawCost: deqNoDrawCost,
|
||||
deqFastCost: deqFastCost,
|
||||
deq: function( self, pxRatio, extent ){
|
||||
return self.dequeue( pxRatio, extent );
|
||||
},
|
||||
onDeqd: function( self, deqd ){
|
||||
for( let i = 0; i < self.onDequeues.length; i++ ){
|
||||
let fn = self.onDequeues[i];
|
||||
|
||||
fn( deqd );
|
||||
}
|
||||
},
|
||||
shouldRedraw: function( self, deqd, pxRatio, extent ){
|
||||
for( let i = 0; i < deqd.length; i++ ){
|
||||
let eles = deqd[i].eles;
|
||||
|
||||
for( let j = 0; j < eles.length; j++ ){
|
||||
let bb = eles[j].boundingBox();
|
||||
|
||||
if( math.boundingBoxesIntersect( bb, extent ) ){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
priority: function( self ){
|
||||
return self.renderer.beforeRenderPriorities.eleTxrDeq;
|
||||
}
|
||||
});
|
||||
|
||||
export default ElementTextureCache;
|
||||
390
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/index.mjs
generated
vendored
Normal file
390
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
The canvas renderer was written by Yue Dong.
|
||||
|
||||
Modifications tracked on Github.
|
||||
*/
|
||||
|
||||
/* global OffscreenCanvas */
|
||||
|
||||
import * as util from '../../../util/index.mjs';
|
||||
import * as is from '../../../is.mjs';
|
||||
import { makeBoundingBox } from '../../../math.mjs';
|
||||
import ElementTextureCache from './ele-texture-cache.mjs';
|
||||
import LayeredTextureCache from './layered-texture-cache.mjs';
|
||||
|
||||
import arrowShapes from './arrow-shapes.mjs';
|
||||
import drawingElements from './drawing-elements.mjs';
|
||||
import drawingEdges from './drawing-edges.mjs';
|
||||
import drawingImages from './drawing-images.mjs';
|
||||
import drawingLabelText from './drawing-label-text.mjs';
|
||||
import drawingNodes from './drawing-nodes.mjs';
|
||||
import drawingRedraw from './drawing-redraw.mjs';
|
||||
import drawingRedrawWebGL from './webgl/drawing-redraw-webgl.mjs';
|
||||
import drawingShapes from './drawing-shapes.mjs';
|
||||
import exportImage from './export-image.mjs';
|
||||
import nodeShapes from './node-shapes.mjs';
|
||||
|
||||
var CR = CanvasRenderer;
|
||||
var CRp = CanvasRenderer.prototype;
|
||||
|
||||
CRp.CANVAS_LAYERS = 3;
|
||||
//
|
||||
CRp.SELECT_BOX = 0;
|
||||
CRp.DRAG = 1;
|
||||
CRp.NODE = 2;
|
||||
CRp.WEBGL = 3;
|
||||
|
||||
CRp.CANVAS_TYPES = [ '2d', '2d', '2d', 'webgl2' ];
|
||||
|
||||
CRp.BUFFER_COUNT = 3;
|
||||
//
|
||||
CRp.TEXTURE_BUFFER = 0;
|
||||
CRp.MOTIONBLUR_BUFFER_NODE = 1;
|
||||
CRp.MOTIONBLUR_BUFFER_DRAG = 2;
|
||||
|
||||
function CanvasRenderer( options ){
|
||||
var r = this;
|
||||
|
||||
var containerWindow = r.cy.window();
|
||||
var document = containerWindow.document;
|
||||
|
||||
if( options.webgl ){
|
||||
CRp.CANVAS_LAYERS = r.CANVAS_LAYERS = 4;
|
||||
console.log('webgl rendering enabled');
|
||||
}
|
||||
|
||||
r.data = {
|
||||
canvases: new Array( CRp.CANVAS_LAYERS ),
|
||||
contexts: new Array( CRp.CANVAS_LAYERS ),
|
||||
canvasNeedsRedraw: new Array( CRp.CANVAS_LAYERS ),
|
||||
|
||||
bufferCanvases: new Array( CRp.BUFFER_COUNT ),
|
||||
bufferContexts: new Array( CRp.CANVAS_LAYERS ),
|
||||
};
|
||||
|
||||
var tapHlOffAttr = '-webkit-tap-highlight-color';
|
||||
var tapHlOffStyle = 'rgba(0,0,0,0)';
|
||||
r.data.canvasContainer = document.createElement( 'div' ); // eslint-disable-line no-undef
|
||||
var containerStyle = r.data.canvasContainer.style;
|
||||
r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
|
||||
containerStyle.position = 'relative';
|
||||
containerStyle.zIndex = '0';
|
||||
containerStyle.overflow = 'hidden';
|
||||
|
||||
var container = options.cy.container();
|
||||
container.appendChild( r.data.canvasContainer );
|
||||
container.style[tapHlOffAttr] = tapHlOffStyle;
|
||||
|
||||
var styleMap = {
|
||||
'-webkit-user-select': 'none',
|
||||
'-moz-user-select': '-moz-none',
|
||||
'user-select': 'none',
|
||||
'-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
|
||||
'outline-style': 'none',
|
||||
};
|
||||
|
||||
if(is.ms()) {
|
||||
styleMap['-ms-touch-action'] = 'none';
|
||||
styleMap['touch-action'] = 'none';
|
||||
}
|
||||
|
||||
for( var i = 0; i < CRp.CANVAS_LAYERS; i++ ){
|
||||
var canvas = r.data.canvases[ i ] = document.createElement( 'canvas' ); // eslint-disable-line no-undef
|
||||
var type = CRp.CANVAS_TYPES[ i ];
|
||||
r.data.contexts[ i ] = canvas.getContext( type );
|
||||
if( !r.data.contexts[ i ] ) {
|
||||
util.error( 'Could not create canvas of type ' + type );
|
||||
}
|
||||
Object.keys(styleMap).forEach((k) => {
|
||||
canvas.style[k] = styleMap[k];
|
||||
});
|
||||
canvas.style.position = 'absolute';
|
||||
canvas.setAttribute( 'data-id', 'layer' + i );
|
||||
canvas.style.zIndex = String( CRp.CANVAS_LAYERS - i );
|
||||
r.data.canvasContainer.appendChild( canvas );
|
||||
|
||||
r.data.canvasNeedsRedraw[ i ] = false;
|
||||
}
|
||||
r.data.topCanvas = r.data.canvases[0];
|
||||
|
||||
r.data.canvases[ CRp.NODE ].setAttribute( 'data-id', 'layer' + CRp.NODE + '-node' );
|
||||
r.data.canvases[ CRp.SELECT_BOX ].setAttribute( 'data-id', 'layer' + CRp.SELECT_BOX + '-selectbox' );
|
||||
r.data.canvases[ CRp.DRAG ].setAttribute( 'data-id', 'layer' + CRp.DRAG + '-drag' );
|
||||
if( r.data.canvases[ CRp.WEBGL ] ) {
|
||||
r.data.canvases[ CRp.WEBGL ].setAttribute( 'data-id', 'layer' + CRp.WEBGL + '-webgl' );
|
||||
}
|
||||
|
||||
for( var i = 0; i < CRp.BUFFER_COUNT; i++ ){
|
||||
r.data.bufferCanvases[ i ] = document.createElement( 'canvas' ); // eslint-disable-line no-undef
|
||||
r.data.bufferContexts[ i ] = r.data.bufferCanvases[ i ].getContext( '2d' );
|
||||
r.data.bufferCanvases[ i ].style.position = 'absolute';
|
||||
r.data.bufferCanvases[ i ].setAttribute( 'data-id', 'buffer' + i );
|
||||
r.data.bufferCanvases[ i ].style.zIndex = String( -i - 1 );
|
||||
r.data.bufferCanvases[ i ].style.visibility = 'hidden';
|
||||
//r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
|
||||
}
|
||||
|
||||
r.pathsEnabled = true;
|
||||
|
||||
let emptyBb = makeBoundingBox();
|
||||
|
||||
let getBoxCenter = bb => ({ x: (bb.x1 + bb.x2)/2, y: (bb.y1 + bb.y2)/2 });
|
||||
|
||||
let getCenterOffset = bb => ({ x: -bb.w/2, y: -bb.h/2 });
|
||||
|
||||
let backgroundTimestampHasChanged = ele => {
|
||||
let _p = ele[0]._private;
|
||||
let same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
|
||||
|
||||
return !same;
|
||||
};
|
||||
|
||||
let getStyleKey = ele => ele[0]._private.nodeKey;
|
||||
let getLabelKey = ele => ele[0]._private.labelStyleKey;
|
||||
let getSourceLabelKey = ele => ele[0]._private.sourceLabelStyleKey;
|
||||
let getTargetLabelKey = ele => ele[0]._private.targetLabelStyleKey;
|
||||
|
||||
let drawElement = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElement( context, ele, bb, false, false, useEleOpacity );
|
||||
let drawLabel = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElementText( context, ele, bb, scaledLabelShown, 'main', useEleOpacity );
|
||||
let drawSourceLabel = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElementText( context, ele, bb, scaledLabelShown, 'source', useEleOpacity );
|
||||
let drawTargetLabel = (context, ele, bb, scaledLabelShown, useEleOpacity) => r.drawElementText( context, ele, bb, scaledLabelShown, 'target', useEleOpacity );
|
||||
|
||||
let getElementBox = ele => { ele.boundingBox(); return ele[0]._private.bodyBounds; };
|
||||
let getLabelBox = ele => { ele.boundingBox(); return ele[0]._private.labelBounds.main || emptyBb; };
|
||||
let getSourceLabelBox = ele => { ele.boundingBox(); return ele[0]._private.labelBounds.source || emptyBb; };
|
||||
let getTargetLabelBox = ele => { ele.boundingBox(); return ele[0]._private.labelBounds.target || emptyBb; };
|
||||
|
||||
let isLabelVisibleAtScale = (ele, scaledLabelShown) => scaledLabelShown;
|
||||
|
||||
let getElementRotationPoint = ele => getBoxCenter( getElementBox(ele) );
|
||||
|
||||
let addTextMargin = (prefix, pt, ele) => {
|
||||
let pre = prefix ? prefix + '-' : '';
|
||||
|
||||
return {
|
||||
x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
|
||||
y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
|
||||
};
|
||||
};
|
||||
|
||||
let getRsPt = (ele, x, y) => {
|
||||
let rs = ele[0]._private.rscratch;
|
||||
|
||||
return { x: rs[x], y: rs[y] };
|
||||
};
|
||||
|
||||
let getLabelRotationPoint = ele => addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
|
||||
let getSourceLabelRotationPoint = ele => addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
|
||||
let getTargetLabelRotationPoint = ele => addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
|
||||
|
||||
let getElementRotationOffset = ele => getCenterOffset( getElementBox(ele) );
|
||||
let getSourceLabelRotationOffset = ele => getCenterOffset( getSourceLabelBox(ele) );
|
||||
let getTargetLabelRotationOffset = ele => getCenterOffset( getTargetLabelBox(ele) );
|
||||
|
||||
let getLabelRotationOffset = ele => {
|
||||
let bb = getLabelBox(ele);
|
||||
let p = getCenterOffset( getLabelBox(ele) );
|
||||
|
||||
if( ele.isNode() ){
|
||||
switch( ele.pstyle('text-halign').value ){
|
||||
case 'left':
|
||||
p.x = -bb.w - (bb.leftPad || 0);
|
||||
break;
|
||||
case 'right':
|
||||
p.x = -(bb.rightPad || 0);
|
||||
break;
|
||||
}
|
||||
|
||||
switch( ele.pstyle('text-valign').value ){
|
||||
case 'top':
|
||||
p.y = -bb.h - (bb.topPad || 0);
|
||||
break;
|
||||
case 'bottom':
|
||||
p.y = -(bb.botPad || 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
};
|
||||
|
||||
let eleTxrCache = r.data.eleTxrCache = new ElementTextureCache( r, {
|
||||
getKey: getStyleKey,
|
||||
doesEleInvalidateKey: backgroundTimestampHasChanged,
|
||||
drawElement: drawElement,
|
||||
getBoundingBox: getElementBox,
|
||||
getRotationPoint: getElementRotationPoint,
|
||||
getRotationOffset: getElementRotationOffset,
|
||||
allowEdgeTxrCaching: false,
|
||||
allowParentTxrCaching: false
|
||||
} );
|
||||
|
||||
let lblTxrCache = r.data.lblTxrCache = new ElementTextureCache( r, {
|
||||
getKey: getLabelKey,
|
||||
drawElement: drawLabel,
|
||||
getBoundingBox: getLabelBox,
|
||||
getRotationPoint: getLabelRotationPoint,
|
||||
getRotationOffset: getLabelRotationOffset,
|
||||
isVisible: isLabelVisibleAtScale
|
||||
} );
|
||||
|
||||
let slbTxrCache = r.data.slbTxrCache = new ElementTextureCache( r, {
|
||||
getKey: getSourceLabelKey,
|
||||
drawElement: drawSourceLabel,
|
||||
getBoundingBox: getSourceLabelBox,
|
||||
getRotationPoint: getSourceLabelRotationPoint,
|
||||
getRotationOffset: getSourceLabelRotationOffset,
|
||||
isVisible: isLabelVisibleAtScale
|
||||
} );
|
||||
|
||||
let tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache( r, {
|
||||
getKey: getTargetLabelKey,
|
||||
drawElement: drawTargetLabel,
|
||||
getBoundingBox: getTargetLabelBox,
|
||||
getRotationPoint: getTargetLabelRotationPoint,
|
||||
getRotationOffset: getTargetLabelRotationOffset,
|
||||
isVisible: isLabelVisibleAtScale
|
||||
} );
|
||||
|
||||
let lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache( r );
|
||||
|
||||
r.onUpdateEleCalcs(function invalidateTextureCaches( willDraw, eles ){
|
||||
// each cache should check for sub-key diff to see that the update affects that cache particularly
|
||||
eleTxrCache.invalidateElements( eles );
|
||||
lblTxrCache.invalidateElements( eles );
|
||||
slbTxrCache.invalidateElements( eles );
|
||||
tlbTxrCache.invalidateElements( eles );
|
||||
|
||||
// any change invalidates the layers
|
||||
lyrTxrCache.invalidateElements( eles );
|
||||
|
||||
// update the old bg timestamp so diffs can be done in the ele txr caches
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let _p = eles[i]._private;
|
||||
|
||||
_p.oldBackgroundTimestamp = _p.backgroundTimestamp;
|
||||
}
|
||||
});
|
||||
|
||||
let refineInLayers = reqs => {
|
||||
for( var i = 0; i < reqs.length; i++ ){
|
||||
lyrTxrCache.enqueueElementRefinement( reqs[i].ele );
|
||||
}
|
||||
};
|
||||
|
||||
eleTxrCache.onDequeue(refineInLayers);
|
||||
lblTxrCache.onDequeue(refineInLayers);
|
||||
slbTxrCache.onDequeue(refineInLayers);
|
||||
tlbTxrCache.onDequeue(refineInLayers);
|
||||
|
||||
if( options.webgl ) {
|
||||
r.initWebgl( options, {
|
||||
getStyleKey,
|
||||
getLabelKey,
|
||||
getSourceLabelKey,
|
||||
getTargetLabelKey,
|
||||
drawElement,
|
||||
drawLabel,
|
||||
drawSourceLabel,
|
||||
drawTargetLabel,
|
||||
getElementBox,
|
||||
getLabelBox,
|
||||
getSourceLabelBox,
|
||||
getTargetLabelBox,
|
||||
getElementRotationPoint,
|
||||
getElementRotationOffset,
|
||||
getLabelRotationPoint,
|
||||
getSourceLabelRotationPoint,
|
||||
getTargetLabelRotationPoint,
|
||||
getLabelRotationOffset,
|
||||
getSourceLabelRotationOffset,
|
||||
getTargetLabelRotationOffset
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
CRp.redrawHint = function( group, bool ){
|
||||
var r = this;
|
||||
|
||||
switch( group ){
|
||||
case 'eles':
|
||||
r.data.canvasNeedsRedraw[ CRp.NODE ] = bool;
|
||||
break;
|
||||
case 'drag':
|
||||
r.data.canvasNeedsRedraw[ CRp.DRAG ] = bool;
|
||||
break;
|
||||
case 'select':
|
||||
r.data.canvasNeedsRedraw[ CRp.SELECT_BOX ] = bool;
|
||||
break;
|
||||
case 'gc':
|
||||
r.data.gc = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// whether to use Path2D caching for drawing
|
||||
var pathsImpld = typeof Path2D !== 'undefined';
|
||||
|
||||
CRp.path2dEnabled = function( on ){
|
||||
if( on === undefined ){
|
||||
return this.pathsEnabled;
|
||||
}
|
||||
|
||||
this.pathsEnabled = on ? true : false;
|
||||
};
|
||||
|
||||
CRp.usePaths = function(){
|
||||
return pathsImpld && this.pathsEnabled;
|
||||
};
|
||||
|
||||
CRp.setImgSmoothing = function( context, bool ){
|
||||
if( context.imageSmoothingEnabled != null ){
|
||||
context.imageSmoothingEnabled = bool;
|
||||
} else {
|
||||
context.webkitImageSmoothingEnabled = bool;
|
||||
context.mozImageSmoothingEnabled = bool;
|
||||
context.msImageSmoothingEnabled = bool;
|
||||
}
|
||||
};
|
||||
|
||||
CRp.getImgSmoothing = function( context ){
|
||||
if( context.imageSmoothingEnabled != null ){
|
||||
return context.imageSmoothingEnabled;
|
||||
} else {
|
||||
return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
|
||||
}
|
||||
};
|
||||
|
||||
CRp.makeOffscreenCanvas = function(width, height){
|
||||
let canvas;
|
||||
|
||||
if( typeof OffscreenCanvas !== typeof undefined ){
|
||||
canvas = new OffscreenCanvas(width, height);
|
||||
} else {
|
||||
var containerWindow = this.cy.window();
|
||||
var document = containerWindow.document;
|
||||
canvas = document.createElement('canvas'); // eslint-disable-line no-undef
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
}
|
||||
|
||||
return canvas;
|
||||
};
|
||||
|
||||
[
|
||||
arrowShapes,
|
||||
drawingElements,
|
||||
drawingEdges,
|
||||
drawingImages,
|
||||
drawingLabelText,
|
||||
drawingNodes,
|
||||
drawingRedraw,
|
||||
drawingRedrawWebGL,
|
||||
drawingShapes,
|
||||
exportImage,
|
||||
nodeShapes
|
||||
].forEach( function( props ){
|
||||
util.extend( CRp, props );
|
||||
} );
|
||||
|
||||
export default CR;
|
||||
1150
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/webgl/drawing-elements-webgl.mjs
generated
vendored
Normal file
1150
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/webgl/drawing-elements-webgl.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
208
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/webgl/misc-upscaler.js
generated
vendored
Normal file
208
frontend/node_modules/cytoscape/src/extensions/renderer/canvas/webgl/misc-upscaler.js
generated
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
// Canvas Upscaling Plugin
|
||||
class MiscUpscaler {
|
||||
constructor(options = {}) {
|
||||
// Plugin Settings
|
||||
this.options = Object.assign({
|
||||
useEdgeDetection: true, // Edge Detection
|
||||
scaleFactor: 2.0, // Upscaling Value
|
||||
}, options);
|
||||
|
||||
this.gl = null;
|
||||
this.program = null;
|
||||
this.frameBuffer = null;
|
||||
this.texture = null;
|
||||
this.outputTexture = null;
|
||||
}
|
||||
|
||||
init(inputCanvas, outputCanvas) {
|
||||
this.inputCanvas = inputCanvas;
|
||||
this.outputCanvas = outputCanvas;
|
||||
|
||||
// Set output canvas size based on scale factor
|
||||
this.outputCanvas.width = this.inputCanvas.width * this.options.scaleFactor;
|
||||
this.outputCanvas.height = this.inputCanvas.height * this.options.scaleFactor;
|
||||
|
||||
// Initialize WebGL context
|
||||
this.gl = this.outputCanvas.getContext('webgl2');
|
||||
if (!this.gl) {
|
||||
console.error('WebGL not supported');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize shaders and buffers
|
||||
this.initShaders();
|
||||
this.initBuffers();
|
||||
this.initTextures();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
initShaders() {
|
||||
// Vertex shader
|
||||
const vertexShaderSource = `
|
||||
attribute vec2 a_position;
|
||||
attribute vec2 a_texCoord;
|
||||
varying vec2 vUv;
|
||||
void main() {
|
||||
vUv = a_texCoord;
|
||||
gl_Position = vec4(a_position, 0.0, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
// Fragment shader
|
||||
const fragmentShaderSource = `
|
||||
precision mediump float;
|
||||
uniform sampler2D tDiffuse;
|
||||
uniform vec2 resolution;
|
||||
varying vec2 vUv;
|
||||
|
||||
void main() {
|
||||
vec2 texelSize = 1.0 / resolution;
|
||||
|
||||
// Get Neighbor Pixels
|
||||
vec4 color = texture2D(tDiffuse, vUv);
|
||||
vec4 colorUp = texture2D(tDiffuse, vUv + vec2(0.0, texelSize.y));
|
||||
vec4 colorDown = texture2D(tDiffuse, vUv - vec2(0.0, texelSize.y));
|
||||
vec4 colorLeft = texture2D(tDiffuse, vUv - vec2(texelSize.x, 0.0));
|
||||
vec4 colorRight = texture2D(tDiffuse, vUv + vec2(texelSize.x, 0.0));
|
||||
|
||||
// Work with edges
|
||||
float edgeStrength = 1.0 - smoothstep(0.1, 0.3, length(color.rgb - colorUp.rgb));
|
||||
edgeStrength += 1.0 - smoothstep(0.1, 0.3, length(color.rgb - colorDown.rgb));
|
||||
edgeStrength += 1.0 - smoothstep(0.1, 0.3, length(color.rgb - colorLeft.rgb));
|
||||
edgeStrength += 1.0 - smoothstep(0.1, 0.3, length(color.rgb - colorRight.rgb));
|
||||
edgeStrength = clamp(edgeStrength, 0.0, 1.0);
|
||||
|
||||
// Applying edges incresing and filtering
|
||||
vec3 enhancedColor = mix(color.rgb, vec3(1.0) - (1.0 - color.rgb) * edgeStrength, 0.5);
|
||||
|
||||
gl_FragColor = vec4(enhancedColor, color.a);
|
||||
}
|
||||
`;
|
||||
|
||||
// Create shader program
|
||||
const vertexShader = this.createShader(this.gl.VERTEX_SHADER, vertexShaderSource);
|
||||
const fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, fragmentShaderSource);
|
||||
this.program = this.createProgram(vertexShader, fragmentShader);
|
||||
|
||||
// Get attribute and uniform locations
|
||||
this.positionLocation = this.gl.getAttribLocation(this.program, 'a_position');
|
||||
this.texCoordLocation = this.gl.getAttribLocation(this.program, 'a_texCoord');
|
||||
this.resolutionLocation = this.gl.getUniformLocation(this.program, 'resolution');
|
||||
this.textureLocation = this.gl.getUniformLocation(this.program, 'tDiffuse');
|
||||
}
|
||||
|
||||
createShader(type, source) {
|
||||
const shader = this.gl.createShader(type);
|
||||
this.gl.shaderSource(shader, source);
|
||||
this.gl.compileShader(shader);
|
||||
|
||||
if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
|
||||
console.error('Shader compile error:', this.gl.getShaderInfoLog(shader));
|
||||
this.gl.deleteShader(shader);
|
||||
return null;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
createProgram(vertexShader, fragmentShader) {
|
||||
const program = this.gl.createProgram();
|
||||
this.gl.attachShader(program, vertexShader);
|
||||
this.gl.attachShader(program, fragmentShader);
|
||||
this.gl.linkProgram(program);
|
||||
|
||||
if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) {
|
||||
console.error('Program link error:', this.gl.getProgramInfoLog(program));
|
||||
return null;
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
initBuffers() {
|
||||
// Create a buffer for positions
|
||||
this.positionBuffer = this.gl.createBuffer();
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
|
||||
|
||||
// Full screen quad (2 triangles)
|
||||
const positions = [
|
||||
-1, -1,
|
||||
1, -1,
|
||||
-1, 1,
|
||||
-1, 1,
|
||||
1, -1,
|
||||
1, 1
|
||||
];
|
||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(positions), this.gl.STATIC_DRAW);
|
||||
|
||||
// Create a buffer for texture coordinates
|
||||
this.texCoordBuffer = this.gl.createBuffer();
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
|
||||
|
||||
// Texture coordinates for the quad with Y-flipped to match canvas coordinates
|
||||
const texCoords = [
|
||||
0, 1, // top-left (flipped from 0,0)
|
||||
1, 1, // top-right (flipped from 1,0)
|
||||
0, 0, // bottom-left (flipped from 0,1)
|
||||
0, 0, // bottom-left (flipped from 0,1)
|
||||
1, 1, // top-right (flipped from 1,0)
|
||||
1, 0 // bottom-right (flipped from 1,1)
|
||||
];
|
||||
this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(texCoords), this.gl.STATIC_DRAW);
|
||||
}
|
||||
|
||||
initTextures() {
|
||||
// Create texture for input canvas
|
||||
this.texture = this.gl.createTexture();
|
||||
this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
|
||||
|
||||
// Set parameters for texture
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
|
||||
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.gl) return;
|
||||
|
||||
// Update texture from input canvas
|
||||
this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
|
||||
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, this.inputCanvas);
|
||||
|
||||
// Set viewport and clear
|
||||
this.gl.viewport(0, 0, this.outputCanvas.width, this.outputCanvas.height);
|
||||
this.gl.clearColor(0, 0, 0, 0);
|
||||
this.gl.clear(this.gl.COLOR_BUFFER_BIT);
|
||||
|
||||
// Use shader program
|
||||
this.gl.useProgram(this.program);
|
||||
|
||||
// Set uniforms
|
||||
this.gl.uniform2f(this.resolutionLocation, this.inputCanvas.width, this.inputCanvas.height);
|
||||
this.gl.uniform1i(this.textureLocation, 0);
|
||||
|
||||
// Set position attribute
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
|
||||
this.gl.enableVertexAttribArray(this.positionLocation);
|
||||
this.gl.vertexAttribPointer(this.positionLocation, 2, this.gl.FLOAT, false, 0, 0);
|
||||
|
||||
// Set texture coordinate attribute
|
||||
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.texCoordBuffer);
|
||||
this.gl.enableVertexAttribArray(this.texCoordLocation);
|
||||
this.gl.vertexAttribPointer(this.texCoordLocation, 2, this.gl.FLOAT, false, 0, 0);
|
||||
|
||||
// Draw
|
||||
this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
resize() {
|
||||
if (this.inputCanvas && this.outputCanvas) {
|
||||
this.outputCanvas.width = this.inputCanvas.width * this.options.scaleFactor;
|
||||
this.outputCanvas.height = this.inputCanvas.height * this.options.scaleFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { MiscUpscaler as UpscalerPlugin };
|
||||
9
frontend/node_modules/cytoscape/src/extensions/renderer/index.mjs
generated
vendored
Normal file
9
frontend/node_modules/cytoscape/src/extensions/renderer/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import nullRenderer from './null/index.mjs';
|
||||
import baseRenderer from './base/index.mjs';
|
||||
import canvasRenderer from './canvas/index.mjs';
|
||||
|
||||
export default [
|
||||
{ name: 'null', impl: nullRenderer },
|
||||
{ name: 'base', impl: baseRenderer },
|
||||
{ name: 'canvas', impl: canvasRenderer },
|
||||
];
|
||||
145
frontend/node_modules/cytoscape/src/is.mjs
generated
vendored
Normal file
145
frontend/node_modules/cytoscape/src/is.mjs
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
/*global HTMLElement DocumentTouch */
|
||||
|
||||
import window from './window.mjs';
|
||||
|
||||
let navigator = window ? window.navigator : null;
|
||||
let document = window ? window.document : null;
|
||||
|
||||
let typeofstr = typeof '';
|
||||
let typeofobj = typeof {};
|
||||
let typeoffn = typeof function(){};
|
||||
let typeofhtmlele = typeof HTMLElement;
|
||||
|
||||
let instanceStr = function( obj ){
|
||||
return obj && obj.instanceString && fn( obj.instanceString ) ? obj.instanceString() : null;
|
||||
};
|
||||
|
||||
export const defined = obj =>
|
||||
obj != null; // not undefined or null
|
||||
|
||||
export const string = obj =>
|
||||
obj != null && typeof obj == typeofstr;
|
||||
|
||||
export const fn = obj =>
|
||||
obj != null && typeof obj === typeoffn;
|
||||
|
||||
export const array = obj =>
|
||||
!(elementOrCollection(obj)) && (Array.isArray ? Array.isArray( obj ) : obj != null && obj instanceof Array);
|
||||
|
||||
export const plainObject = obj =>
|
||||
obj != null && typeof obj === typeofobj && !array( obj ) && obj.constructor === Object;
|
||||
|
||||
export const object = obj =>
|
||||
obj != null && typeof obj === typeofobj;
|
||||
|
||||
export const number = obj =>
|
||||
obj != null && typeof obj === typeof 1 && !isNaN( obj );
|
||||
|
||||
export const integer = obj =>
|
||||
number( obj ) && Math.floor( obj ) === obj;
|
||||
|
||||
export const bool = obj =>
|
||||
obj != null && typeof obj === typeof true;
|
||||
|
||||
export const htmlElement = obj => {
|
||||
if( 'undefined' === typeofhtmlele ){
|
||||
return undefined;
|
||||
} else {
|
||||
return null != obj && obj instanceof HTMLElement;
|
||||
}
|
||||
};
|
||||
|
||||
export const elementOrCollection = obj =>
|
||||
element( obj ) || collection( obj );
|
||||
|
||||
export const element = obj =>
|
||||
instanceStr( obj ) === 'collection' && obj._private.single;
|
||||
|
||||
export const collection = obj =>
|
||||
instanceStr( obj ) === 'collection' && !obj._private.single;
|
||||
|
||||
export const core = obj =>
|
||||
instanceStr( obj ) === 'core';
|
||||
|
||||
export const style = obj =>
|
||||
instanceStr( obj ) === 'style';
|
||||
|
||||
export const stylesheet = obj =>
|
||||
instanceStr( obj ) === 'stylesheet';
|
||||
|
||||
export const event = obj =>
|
||||
instanceStr( obj ) === 'event';
|
||||
|
||||
export const thread = obj =>
|
||||
instanceStr( obj ) === 'thread';
|
||||
|
||||
export const fabric = obj =>
|
||||
instanceStr( obj ) === 'fabric';
|
||||
|
||||
export const emptyString = obj => {
|
||||
if( obj === undefined || obj === null ){ // null is empty
|
||||
return true;
|
||||
} else if( obj === '' || obj.match( /^\s+$/ ) ){
|
||||
return true; // empty string is empty
|
||||
}
|
||||
|
||||
return false; // otherwise, we don't know what we've got
|
||||
};
|
||||
|
||||
export const nonemptyString = obj => {
|
||||
if( obj && string( obj ) && obj !== '' && !obj.match( /^\s+$/ ) ){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const domElement = obj => {
|
||||
if( typeof HTMLElement === 'undefined' ){
|
||||
return false; // we're not in a browser so it doesn't matter
|
||||
} else {
|
||||
return obj instanceof HTMLElement;
|
||||
}
|
||||
};
|
||||
|
||||
export const boundingBox = obj =>
|
||||
plainObject( obj ) &&
|
||||
number( obj.x1 ) && number( obj.x2 ) &&
|
||||
number( obj.y1 ) && number( obj.y2 )
|
||||
;
|
||||
|
||||
export const promise = obj =>
|
||||
object( obj ) && fn( obj.then );
|
||||
|
||||
export const touch = () =>
|
||||
window && ( ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch );
|
||||
|
||||
export const gecko = () =>
|
||||
window && ( typeof InstallTrigger !== 'undefined' || ('MozAppearance' in document.documentElement.style) );
|
||||
|
||||
export const webkit = () =>
|
||||
window && ( typeof webkitURL !== 'undefined' || ('WebkitAppearance' in document.documentElement.style) );
|
||||
|
||||
export const chromium = () =>
|
||||
window && ( typeof chrome !== 'undefined' );
|
||||
|
||||
export const khtml = () =>
|
||||
navigator && navigator.vendor.match( /kde/i ); // probably a better way to detect this...
|
||||
|
||||
export const khtmlEtc = () =>
|
||||
khtml() || webkit() || chromium();
|
||||
|
||||
export const ms = () =>
|
||||
navigator && navigator.userAgent.match( /msie|trident|edge/i ); // probably a better way to detect this...
|
||||
|
||||
export const windows = () =>
|
||||
navigator && navigator.appVersion.match( /Win/i );
|
||||
|
||||
export const mac = () =>
|
||||
navigator && navigator.appVersion.match( /Mac/i );
|
||||
|
||||
export const linux = () =>
|
||||
navigator && navigator.appVersion.match( /Linux/i );
|
||||
|
||||
export const unix = () =>
|
||||
navigator && navigator.appVersion.match( /X11/i );
|
||||
13
frontend/node_modules/cytoscape/src/selector/new-query.mjs
generated
vendored
Normal file
13
frontend/node_modules/cytoscape/src/selector/new-query.mjs
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Make a new query object
|
||||
*
|
||||
* @prop type {Type} The type enum (int) of the query
|
||||
* @prop checks List of checks to make against an ele to test for a match
|
||||
*/
|
||||
let newQuery = function(){
|
||||
return {
|
||||
checks: []
|
||||
};
|
||||
};
|
||||
|
||||
export default newQuery;
|
||||
254
frontend/node_modules/cytoscape/src/selector/parse.mjs
generated
vendored
Normal file
254
frontend/node_modules/cytoscape/src/selector/parse.mjs
generated
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
import { warn } from '../util/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import exprs from './expressions.mjs';
|
||||
import newQuery from './new-query.mjs';
|
||||
import Type from './type.mjs';
|
||||
|
||||
/**
|
||||
* Of all the expressions, find the first match in the remaining text.
|
||||
* @param {string} remaining The remaining text to parse
|
||||
* @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
|
||||
*/
|
||||
const consumeExpr = ( remaining ) => {
|
||||
let expr;
|
||||
let match;
|
||||
let name;
|
||||
|
||||
for( let j = 0; j < exprs.length; j++ ){
|
||||
let e = exprs[ j ];
|
||||
let n = e.name;
|
||||
|
||||
let m = remaining.match( e.regexObj );
|
||||
|
||||
if( m != null ){
|
||||
match = m;
|
||||
expr = e;
|
||||
name = n;
|
||||
|
||||
let consumed = m[0];
|
||||
remaining = remaining.substring( consumed.length );
|
||||
|
||||
break; // we've consumed one expr, so we can return now
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
expr: expr,
|
||||
match: match,
|
||||
name: name,
|
||||
remaining: remaining
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Consume all the leading whitespace
|
||||
* @param {string} remaining The text to consume
|
||||
* @returns The text with the leading whitespace removed
|
||||
*/
|
||||
const consumeWhitespace = ( remaining ) => {
|
||||
let match = remaining.match( /^\s+/ );
|
||||
|
||||
if( match ){
|
||||
let consumed = match[0];
|
||||
remaining = remaining.substring( consumed.length );
|
||||
}
|
||||
|
||||
return remaining;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the string and store the parsed representation in the Selector.
|
||||
* @param {string} selector The selector string
|
||||
* @returns `true` if the selector was successfully parsed, `false` otherwise
|
||||
*/
|
||||
const parse = function( selector ){
|
||||
let self = this;
|
||||
|
||||
let remaining = self.inputText = selector;
|
||||
|
||||
let currentQuery = self[0] = newQuery();
|
||||
self.length = 1;
|
||||
|
||||
remaining = consumeWhitespace( remaining ); // get rid of leading whitespace
|
||||
|
||||
for( ;; ){
|
||||
let exprInfo = consumeExpr( remaining );
|
||||
|
||||
if( exprInfo.expr == null ){
|
||||
warn( 'The selector `' + selector + '`is invalid' );
|
||||
return false;
|
||||
} else {
|
||||
let args = exprInfo.match.slice( 1 );
|
||||
|
||||
// let the token populate the selector object in currentQuery
|
||||
let ret = exprInfo.expr.populate( self, currentQuery, args );
|
||||
|
||||
if( ret === false ){
|
||||
return false; // exit if population failed
|
||||
} else if( ret != null ){
|
||||
currentQuery = ret; // change the current query to be filled if the expr specifies
|
||||
}
|
||||
}
|
||||
|
||||
remaining = exprInfo.remaining;
|
||||
|
||||
// we're done when there's nothing left to parse
|
||||
if( remaining.match( /^\s*$/ ) ){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let lastQ = self[self.length - 1];
|
||||
|
||||
if( self.currentSubject != null ){
|
||||
lastQ.subject = self.currentSubject;
|
||||
}
|
||||
|
||||
lastQ.edgeCount = self.edgeCount;
|
||||
lastQ.compoundCount = self.compoundCount;
|
||||
|
||||
for( let i = 0; i < self.length; i++ ){
|
||||
let q = self[i];
|
||||
|
||||
// in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
|
||||
if( q.compoundCount > 0 && q.edgeCount > 0 ){
|
||||
warn( 'The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector' );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( q.edgeCount > 1 ){
|
||||
warn( 'The selector `' + selector + '` is invalid because it uses multiple edge selectors' );
|
||||
return false;
|
||||
} else if( q.edgeCount === 1 ){
|
||||
warn( 'The selector `' + selector + '` is deprecated. Edge selectors do not take effect on changes to source and target nodes after an edge is added, for performance reasons. Use a class or data selector on edges instead, updating the class or data of an edge when your app detects a change in source or target nodes.' );
|
||||
}
|
||||
}
|
||||
|
||||
return true; // success
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the selector represented as a string. This value uses default formatting,
|
||||
* so things like spacing may differ from the input text passed to the constructor.
|
||||
* @returns {string} The selector string
|
||||
*/
|
||||
export const toString = function(){
|
||||
if( this.toStringCache != null ){
|
||||
return this.toStringCache;
|
||||
}
|
||||
|
||||
let clean = function( obj ){
|
||||
if( obj == null ){
|
||||
return '';
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
let cleanVal = function( val ){
|
||||
if( is.string( val ) ){
|
||||
return '"' + val + '"';
|
||||
} else {
|
||||
return clean( val );
|
||||
}
|
||||
};
|
||||
|
||||
let space = ( val ) => {
|
||||
return ' ' + val + ' ';
|
||||
};
|
||||
|
||||
let checkToString = ( check, subject ) => {
|
||||
let { type, value } = check;
|
||||
|
||||
switch( type ){
|
||||
case Type.GROUP: {
|
||||
let group = clean( value );
|
||||
|
||||
return group.substring( 0, group.length - 1 );
|
||||
}
|
||||
|
||||
case Type.DATA_COMPARE: {
|
||||
let { field, operator } = check;
|
||||
|
||||
return '[' + field + space( clean( operator ) ) + cleanVal( value ) + ']';
|
||||
}
|
||||
|
||||
case Type.DATA_BOOL: {
|
||||
let { operator, field } = check;
|
||||
|
||||
return '[' + clean( operator ) + field + ']';
|
||||
}
|
||||
|
||||
case Type.DATA_EXIST: {
|
||||
let { field } = check;
|
||||
|
||||
return '[' + field + ']';
|
||||
}
|
||||
|
||||
case Type.META_COMPARE: {
|
||||
let { operator, field } = check;
|
||||
|
||||
return '[[' + field + space( clean( operator ) ) + cleanVal( value ) + ']]';
|
||||
}
|
||||
|
||||
case Type.STATE: {
|
||||
return value;
|
||||
}
|
||||
|
||||
case Type.ID: {
|
||||
return '#' + value;
|
||||
}
|
||||
|
||||
case Type.CLASS: {
|
||||
return '.' + value;
|
||||
}
|
||||
|
||||
case Type.PARENT:
|
||||
case Type.CHILD: {
|
||||
return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
|
||||
}
|
||||
|
||||
case Type.ANCESTOR:
|
||||
case Type.DESCENDANT: {
|
||||
return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
|
||||
}
|
||||
|
||||
case Type.COMPOUND_SPLIT: {
|
||||
let lhs = queryToString(check.left, subject);
|
||||
let sub = queryToString(check.subject, subject);
|
||||
let rhs = queryToString(check.right, subject);
|
||||
|
||||
return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
|
||||
}
|
||||
|
||||
case Type.TRUE: {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let queryToString = ( query, subject ) => {
|
||||
return query.checks.reduce((str, chk, i) => {
|
||||
return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
|
||||
}, '');
|
||||
};
|
||||
|
||||
let str = '';
|
||||
|
||||
for( let i = 0; i < this.length; i++ ){
|
||||
let query = this[ i ];
|
||||
|
||||
str += queryToString( query, query.subject );
|
||||
|
||||
if( this.length > 1 && i < this.length - 1 ){
|
||||
str += ', ';
|
||||
}
|
||||
}
|
||||
|
||||
this.toStringCache = str;
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
export default { parse, toString };
|
||||
122
frontend/node_modules/cytoscape/src/selector/query-type-match.mjs
generated
vendored
Normal file
122
frontend/node_modules/cytoscape/src/selector/query-type-match.mjs
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
import Type from './type.mjs';
|
||||
import { stateSelectorMatches } from './state.mjs';
|
||||
import { valCmp, boolCmp, existCmp, meta, data } from './data.mjs';
|
||||
|
||||
/** A lookup of `match(check, ele)` functions by `Type` int */
|
||||
export const match = [];
|
||||
|
||||
/**
|
||||
* Returns whether the query matches for the element
|
||||
* @param query The `{ type, value, ... }` query object
|
||||
* @param ele The element to compare against
|
||||
*/
|
||||
export const matches = (query, ele) => {
|
||||
return query.checks.every( chk => match[chk.type](chk, ele) );
|
||||
};
|
||||
|
||||
match[Type.GROUP] = (check, ele) => {
|
||||
let group = check.value;
|
||||
|
||||
return group === '*' || group === ele.group();
|
||||
};
|
||||
|
||||
match[Type.STATE] = (check, ele) => {
|
||||
let stateSelector = check.value;
|
||||
|
||||
return stateSelectorMatches( stateSelector, ele );
|
||||
};
|
||||
|
||||
match[Type.ID] = (check, ele) => {
|
||||
let id = check.value;
|
||||
|
||||
return ele.id() === id;
|
||||
};
|
||||
|
||||
match[Type.CLASS] = (check, ele) => {
|
||||
let cls = check.value;
|
||||
|
||||
return ele.hasClass(cls);
|
||||
};
|
||||
|
||||
match[Type.META_COMPARE] = (check, ele) => {
|
||||
let { field, operator, value } = check;
|
||||
|
||||
return valCmp( meta(ele, field), operator, value );
|
||||
};
|
||||
|
||||
match[Type.DATA_COMPARE] = (check, ele) => {
|
||||
let { field, operator, value } = check;
|
||||
|
||||
return valCmp( data(ele, field), operator, value );
|
||||
};
|
||||
|
||||
match[Type.DATA_BOOL] = (check, ele) => {
|
||||
let { field, operator } = check;
|
||||
|
||||
return boolCmp( data(ele, field), operator );
|
||||
};
|
||||
|
||||
match[Type.DATA_EXIST] = (check, ele) => {
|
||||
let { field, operator } = check;
|
||||
|
||||
return existCmp( data(ele, field), operator );
|
||||
};
|
||||
|
||||
match[Type.UNDIRECTED_EDGE] = (check, ele) => {
|
||||
let qA = check.nodes[0];
|
||||
let qB = check.nodes[1];
|
||||
let src = ele.source();
|
||||
let tgt = ele.target();
|
||||
|
||||
return ( matches(qA, src) && matches(qB, tgt) ) || ( matches(qB, src) && matches(qA, tgt) );
|
||||
};
|
||||
|
||||
match[Type.NODE_NEIGHBOR] = (check, ele) => {
|
||||
return matches(check.node, ele) && ele.neighborhood().some( n => n.isNode() && matches(check.neighbor, n) );
|
||||
};
|
||||
|
||||
match[Type.DIRECTED_EDGE] = (check, ele) => {
|
||||
return matches(check.source, ele.source()) && matches(check.target, ele.target());
|
||||
};
|
||||
|
||||
match[Type.NODE_SOURCE] = (check, ele) => {
|
||||
return matches(check.source, ele) && ele.outgoers().some( n => n.isNode() && matches(check.target, n) );
|
||||
};
|
||||
|
||||
match[Type.NODE_TARGET] = (check, ele) => {
|
||||
return matches(check.target, ele) && ele.incomers().some( n => n.isNode() && matches(check.source, n) );
|
||||
};
|
||||
|
||||
match[Type.CHILD] = (check, ele) => {
|
||||
return matches(check.child, ele) && matches(check.parent, ele.parent());
|
||||
};
|
||||
|
||||
match[Type.PARENT] = (check, ele) => {
|
||||
return matches(check.parent, ele) && ele.children().some( c => matches(check.child, c) );
|
||||
};
|
||||
|
||||
match[Type.DESCENDANT] = (check, ele) => {
|
||||
return matches(check.descendant, ele) && ele.ancestors().some( a => matches(check.ancestor, a) );
|
||||
};
|
||||
|
||||
match[Type.ANCESTOR] = (check, ele) => {
|
||||
return matches(check.ancestor, ele) && ele.descendants().some( d => matches(check.descendant, d) );
|
||||
};
|
||||
|
||||
match[Type.COMPOUND_SPLIT] = (check, ele) => {
|
||||
return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
|
||||
};
|
||||
|
||||
match[Type.TRUE] = () => true;
|
||||
|
||||
match[Type.COLLECTION] = (check, ele) => {
|
||||
let collection = check.value;
|
||||
|
||||
return collection.has(ele);
|
||||
};
|
||||
|
||||
match[Type.FILTER] = (check, ele) => {
|
||||
let filter = check.value;
|
||||
|
||||
return filter(ele);
|
||||
};
|
||||
849
frontend/node_modules/cytoscape/src/style/apply.mjs
generated
vendored
Normal file
849
frontend/node_modules/cytoscape/src/style/apply.mjs
generated
vendored
Normal file
@@ -0,0 +1,849 @@
|
||||
import * as util from '../util/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import Promise from '../promise.mjs';
|
||||
|
||||
const styfn = {};
|
||||
|
||||
// keys for style blocks, e.g. ttfftt
|
||||
const TRUE = 't';
|
||||
const FALSE = 'f';
|
||||
|
||||
// (potentially expensive calculation)
|
||||
// apply the style to the element based on
|
||||
// - its bypass
|
||||
// - what selectors match it
|
||||
styfn.apply = function( eles ){
|
||||
let self = this;
|
||||
let _p = self._private;
|
||||
let cy = _p.cy;
|
||||
let updatedEles = cy.collection();
|
||||
|
||||
for( let ie = 0; ie < eles.length; ie++ ){
|
||||
let ele = eles[ ie ];
|
||||
let cxtMeta = self.getContextMeta( ele );
|
||||
|
||||
if( cxtMeta.empty ){
|
||||
continue;
|
||||
}
|
||||
|
||||
let cxtStyle = self.getContextStyle( cxtMeta );
|
||||
let app = self.applyContextStyle( cxtMeta, cxtStyle, ele );
|
||||
|
||||
if( ele._private.appliedInitStyle ){
|
||||
self.updateTransitions( ele, app.diffProps );
|
||||
} else {
|
||||
ele._private.appliedInitStyle = true;
|
||||
}
|
||||
|
||||
let hintsDiff = self.updateStyleHints( ele );
|
||||
|
||||
if( hintsDiff ){
|
||||
updatedEles.push( ele );
|
||||
}
|
||||
|
||||
} // for elements
|
||||
|
||||
return updatedEles;
|
||||
};
|
||||
|
||||
styfn.getPropertiesDiff = function( oldCxtKey, newCxtKey ){
|
||||
let self = this;
|
||||
let cache = self._private.propDiffs = self._private.propDiffs || {};
|
||||
let dualCxtKey = oldCxtKey + '-' + newCxtKey;
|
||||
let cachedVal = cache[ dualCxtKey ];
|
||||
|
||||
if( cachedVal ){
|
||||
return cachedVal;
|
||||
}
|
||||
|
||||
let diffProps = [];
|
||||
let addedProp = {};
|
||||
|
||||
for( let i = 0; i < self.length; i++ ){
|
||||
let cxt = self[ i ];
|
||||
let oldHasCxt = oldCxtKey[ i ] === TRUE;
|
||||
let newHasCxt = newCxtKey[ i ] === TRUE;
|
||||
let cxtHasDiffed = oldHasCxt !== newHasCxt;
|
||||
let cxtHasMappedProps = cxt.mappedProperties.length > 0;
|
||||
|
||||
if( cxtHasDiffed || ( newHasCxt && cxtHasMappedProps )){
|
||||
let props;
|
||||
|
||||
if( cxtHasDiffed && cxtHasMappedProps ){
|
||||
props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
|
||||
} else if( cxtHasDiffed ){
|
||||
props = cxt.properties; // need to check them all
|
||||
} else if( cxtHasMappedProps ){
|
||||
props = cxt.mappedProperties; // only need to check mapped
|
||||
}
|
||||
|
||||
for( let j = 0; j < props.length; j++ ){
|
||||
let prop = props[ j ];
|
||||
let name = prop.name;
|
||||
|
||||
// if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
|
||||
// (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
|
||||
// is cached)
|
||||
let laterCxtOverrides = false;
|
||||
for( let k = i + 1; k < self.length; k++ ){
|
||||
let laterCxt = self[ k ];
|
||||
let hasLaterCxt = newCxtKey[ k ] === TRUE;
|
||||
|
||||
if( !hasLaterCxt ){ continue; } // can't override unless the context is active
|
||||
|
||||
laterCxtOverrides = laterCxt.properties[ prop.name ] != null;
|
||||
|
||||
if( laterCxtOverrides ){ break; } // exit early as long as one later context overrides
|
||||
}
|
||||
|
||||
if( !addedProp[ name ] && !laterCxtOverrides ){
|
||||
addedProp[ name ] = true;
|
||||
diffProps.push( name );
|
||||
}
|
||||
} // for props
|
||||
} // if
|
||||
|
||||
} // for contexts
|
||||
|
||||
cache[ dualCxtKey ] = diffProps;
|
||||
return diffProps;
|
||||
};
|
||||
|
||||
styfn.getContextMeta = function( ele ){
|
||||
let self = this;
|
||||
let cxtKey = '';
|
||||
let diffProps;
|
||||
let prevKey = ele._private.styleCxtKey || '';
|
||||
|
||||
// get the cxt key
|
||||
for( let i = 0; i < self.length; i++ ){
|
||||
let context = self[ i ];
|
||||
let contextSelectorMatches = context.selector && context.selector.matches( ele ); // NB: context.selector may be null for 'core'
|
||||
|
||||
if( contextSelectorMatches ){
|
||||
cxtKey += TRUE;
|
||||
} else {
|
||||
cxtKey += FALSE;
|
||||
}
|
||||
} // for context
|
||||
|
||||
diffProps = self.getPropertiesDiff( prevKey, cxtKey );
|
||||
|
||||
ele._private.styleCxtKey = cxtKey;
|
||||
|
||||
return {
|
||||
key: cxtKey,
|
||||
diffPropNames: diffProps,
|
||||
empty: diffProps.length === 0
|
||||
};
|
||||
};
|
||||
|
||||
// gets a computed ele style object based on matched contexts
|
||||
styfn.getContextStyle = function( cxtMeta ){
|
||||
let cxtKey = cxtMeta.key;
|
||||
let self = this;
|
||||
let cxtStyles = this._private.contextStyles = this._private.contextStyles || {};
|
||||
|
||||
// if already computed style, returned cached copy
|
||||
if( cxtStyles[ cxtKey ] ){ return cxtStyles[ cxtKey ]; }
|
||||
|
||||
let style = {
|
||||
_private: {
|
||||
key: cxtKey
|
||||
}
|
||||
};
|
||||
|
||||
for( let i = 0; i < self.length; i++ ){
|
||||
let cxt = self[ i ];
|
||||
let hasCxt = cxtKey[ i ] === TRUE;
|
||||
|
||||
if( !hasCxt ){ continue; }
|
||||
|
||||
for( let j = 0; j < cxt.properties.length; j++ ){
|
||||
let prop = cxt.properties[ j ];
|
||||
|
||||
style[ prop.name ] = prop;
|
||||
}
|
||||
}
|
||||
|
||||
cxtStyles[ cxtKey ] = style;
|
||||
return style;
|
||||
};
|
||||
|
||||
styfn.applyContextStyle = function( cxtMeta, cxtStyle, ele ){
|
||||
let self = this;
|
||||
let diffProps = cxtMeta.diffPropNames;
|
||||
let retDiffProps = {};
|
||||
let types = self.types;
|
||||
|
||||
for( let i = 0; i < diffProps.length; i++ ){
|
||||
let diffPropName = diffProps[ i ];
|
||||
let cxtProp = cxtStyle[ diffPropName ];
|
||||
let eleProp = ele.pstyle( diffPropName );
|
||||
|
||||
if( !cxtProp ){ // no context prop means delete
|
||||
if( !eleProp ){
|
||||
continue; // no existing prop means nothing needs to be removed
|
||||
// nb affects initial application on mapped values like control-point-distances
|
||||
} else if( eleProp.bypass ){
|
||||
cxtProp = { name: diffPropName, deleteBypassed: true };
|
||||
} else {
|
||||
cxtProp = { name: diffPropName, delete: true };
|
||||
}
|
||||
}
|
||||
|
||||
// save cycles when the context prop doesn't need to be applied
|
||||
if( eleProp === cxtProp ){ continue; }
|
||||
|
||||
// save cycles when a mapped context prop doesn't need to be applied
|
||||
if(
|
||||
cxtProp.mapped === types.fn // context prop is function mapper
|
||||
&& eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
|
||||
&& eleProp.mapping != null // ele prop is a concrete value from from a mapper
|
||||
&& eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
|
||||
){ // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
|
||||
let mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
|
||||
let fnValue = mapping.fnValue = cxtProp.value( ele ); // temporarily cache the value in case of a miss
|
||||
|
||||
if( fnValue === mapping.prevFnValue ){ continue; }
|
||||
}
|
||||
|
||||
let retDiffProp = retDiffProps[ diffPropName ] = {
|
||||
prev: eleProp
|
||||
};
|
||||
|
||||
self.applyParsedProperty( ele, cxtProp );
|
||||
|
||||
retDiffProp.next = ele.pstyle( diffPropName );
|
||||
|
||||
if( retDiffProp.next && retDiffProp.next.bypass ){
|
||||
retDiffProp.next = retDiffProp.next.bypassed;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
diffProps: retDiffProps
|
||||
};
|
||||
};
|
||||
|
||||
styfn.updateStyleHints = function(ele){
|
||||
let _p = ele._private;
|
||||
let self = this;
|
||||
let propNames = self.propertyGroupNames;
|
||||
let propGrKeys = self.propertyGroupKeys;
|
||||
let propHash = ( ele, propNames, seedKey ) => self.getPropertiesHash( ele, propNames, seedKey );
|
||||
let oldStyleKey = _p.styleKey;
|
||||
|
||||
if( ele.removed() ){ return false; }
|
||||
|
||||
let isNode = _p.group === 'nodes';
|
||||
|
||||
// get the style key hashes per prop group
|
||||
// but lazily -- only use non-default prop values to reduce the number of hashes
|
||||
//
|
||||
|
||||
let overriddenStyles = ele._private.style;
|
||||
|
||||
propNames = Object.keys( overriddenStyles );
|
||||
|
||||
for( let i = 0; i < propGrKeys.length; i++ ){
|
||||
let grKey = propGrKeys[i];
|
||||
|
||||
_p.styleKeys[ grKey ] = [ util.DEFAULT_HASH_SEED, util.DEFAULT_HASH_SEED_ALT ];
|
||||
}
|
||||
|
||||
let updateGrKey1 = (val, grKey) => _p.styleKeys[ grKey ][0] = util.hashInt( val, _p.styleKeys[ grKey ][0] );
|
||||
let updateGrKey2 = (val, grKey) => _p.styleKeys[ grKey ][1] = util.hashIntAlt( val, _p.styleKeys[ grKey ][1] );
|
||||
|
||||
let updateGrKey = (val, grKey) => {
|
||||
updateGrKey1(val, grKey);
|
||||
updateGrKey2(val, grKey);
|
||||
};
|
||||
|
||||
let updateGrKeyWStr = (strVal, grKey) => {
|
||||
for( let j = 0; j < strVal.length; j++ ){
|
||||
let ch = strVal.charCodeAt(j);
|
||||
|
||||
updateGrKey1(ch, grKey);
|
||||
updateGrKey2(ch, grKey);
|
||||
}
|
||||
};
|
||||
|
||||
// - hashing works on 32 bit ints b/c we use bitwise ops
|
||||
// - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
|
||||
// - raise up small numbers so more significant digits are seen by hashing
|
||||
// - make small numbers larger than a normal value to avoid collisions
|
||||
// - works in practice and it's relatively cheap
|
||||
let N = 2000000000;
|
||||
let cleanNum = val => (-128 < val && val < 128) && Math.floor(val) !== val ? N - ((val * 1024) | 0) : val;
|
||||
|
||||
for( let i = 0; i < propNames.length; i++ ){
|
||||
let name = propNames[i];
|
||||
let parsedProp = overriddenStyles[ name ];
|
||||
|
||||
if( parsedProp == null ){ continue; }
|
||||
|
||||
let propInfo = this.properties[name];
|
||||
let type = propInfo.type;
|
||||
let grKey = propInfo.groupKey;
|
||||
let normalizedNumberVal;
|
||||
|
||||
if( propInfo.hashOverride != null ){
|
||||
normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
|
||||
} else if( parsedProp.pfValue != null ){
|
||||
normalizedNumberVal = parsedProp.pfValue;
|
||||
}
|
||||
|
||||
// might not be a number if it allows enums
|
||||
let numberVal = propInfo.enums == null ? parsedProp.value : null;
|
||||
let haveNormNum = normalizedNumberVal != null;
|
||||
let haveUnitedNum = numberVal != null;
|
||||
let haveNum = haveNormNum || haveUnitedNum;
|
||||
let units = parsedProp.units;
|
||||
|
||||
// numbers are cheaper to hash than strings
|
||||
// 1 hash op vs n hash ops (for length n string)
|
||||
if( type.number && haveNum && !type.multiple ){
|
||||
let v = haveNormNum ? normalizedNumberVal : numberVal;
|
||||
|
||||
updateGrKey(cleanNum(v), grKey);
|
||||
|
||||
if( !haveNormNum && units != null ){
|
||||
updateGrKeyWStr(units, grKey);
|
||||
}
|
||||
} else {
|
||||
updateGrKeyWStr(parsedProp.strValue, grKey);
|
||||
}
|
||||
}
|
||||
|
||||
// overall style key
|
||||
//
|
||||
|
||||
let hash = [ util.DEFAULT_HASH_SEED, util.DEFAULT_HASH_SEED_ALT ];
|
||||
|
||||
for( let i = 0; i < propGrKeys.length; i++ ){
|
||||
let grKey = propGrKeys[i];
|
||||
let grHash = _p.styleKeys[ grKey ];
|
||||
|
||||
hash[0] = util.hashInt( grHash[0], hash[0] );
|
||||
hash[1] = util.hashIntAlt( grHash[1], hash[1] );
|
||||
}
|
||||
|
||||
_p.styleKey = util.combineHashes(hash[0], hash[1]);
|
||||
|
||||
// label dims
|
||||
//
|
||||
|
||||
let sk = _p.styleKeys;
|
||||
|
||||
_p.labelDimsKey = util.combineHashesArray(sk.labelDimensions);
|
||||
|
||||
let labelKeys = propHash( ele, ['label'], sk.labelDimensions );
|
||||
|
||||
_p.labelKey = util.combineHashesArray(labelKeys);
|
||||
_p.labelStyleKey = util.combineHashesArray(util.hashArrays(sk.commonLabel, labelKeys));
|
||||
|
||||
if( !isNode ){
|
||||
let sourceLabelKeys = propHash( ele, ['source-label'], sk.labelDimensions );
|
||||
_p.sourceLabelKey = util.combineHashesArray(sourceLabelKeys);
|
||||
_p.sourceLabelStyleKey = util.combineHashesArray(util.hashArrays(sk.commonLabel, sourceLabelKeys));
|
||||
|
||||
let targetLabelKeys = propHash( ele, ['target-label'], sk.labelDimensions );
|
||||
_p.targetLabelKey = util.combineHashesArray(targetLabelKeys);
|
||||
_p.targetLabelStyleKey = util.combineHashesArray(util.hashArrays(sk.commonLabel, targetLabelKeys));
|
||||
}
|
||||
|
||||
// node
|
||||
//
|
||||
|
||||
if( isNode ){
|
||||
let { nodeBody, nodeBorder, nodeOutline, backgroundImage, compound, pie, stripe } = _p.styleKeys;
|
||||
|
||||
let nodeKeys = [ nodeBody, nodeBorder, nodeOutline, backgroundImage, compound, pie, stripe ].filter(k => k != null).reduce(util.hashArrays, [
|
||||
util.DEFAULT_HASH_SEED,
|
||||
util.DEFAULT_HASH_SEED_ALT
|
||||
]);
|
||||
_p.nodeKey = util.combineHashesArray(nodeKeys);
|
||||
|
||||
_p.hasPie = pie != null && pie[0] !== util.DEFAULT_HASH_SEED && pie[1] !== util.DEFAULT_HASH_SEED_ALT;
|
||||
|
||||
_p.hasStripe = stripe != null && stripe[0] !== util.DEFAULT_HASH_SEED && stripe[1] !== util.DEFAULT_HASH_SEED_ALT;
|
||||
}
|
||||
|
||||
return oldStyleKey !== _p.styleKey;
|
||||
};
|
||||
|
||||
styfn.clearStyleHints = function(ele){
|
||||
let _p = ele._private;
|
||||
|
||||
_p.styleCxtKey = '';
|
||||
_p.styleKeys = {};
|
||||
_p.styleKey = null;
|
||||
_p.labelKey = null;
|
||||
_p.labelStyleKey = null;
|
||||
_p.sourceLabelKey = null;
|
||||
_p.sourceLabelStyleKey = null;
|
||||
_p.targetLabelKey = null;
|
||||
_p.targetLabelStyleKey = null;
|
||||
_p.nodeKey = null;
|
||||
_p.hasPie = null;
|
||||
_p.hasStripe = null;
|
||||
};
|
||||
|
||||
// apply a property to the style (for internal use)
|
||||
// returns whether application was successful
|
||||
//
|
||||
// now, this function flattens the property, and here's how:
|
||||
//
|
||||
// for parsedProp:{ bypass: true, deleteBypass: true }
|
||||
// no property is generated, instead the bypass property in the
|
||||
// element's style is replaced by what's pointed to by the `bypassed`
|
||||
// field in the bypass property (i.e. restoring the property the
|
||||
// bypass was overriding)
|
||||
//
|
||||
// for parsedProp:{ mapped: truthy }
|
||||
// the generated flattenedProp:{ mapping: prop }
|
||||
//
|
||||
// for parsedProp:{ bypass: true }
|
||||
// the generated flattenedProp:{ bypassed: parsedProp }
|
||||
styfn.applyParsedProperty = function( ele, parsedProp ){
|
||||
let self = this;
|
||||
let prop = parsedProp;
|
||||
let style = ele._private.style;
|
||||
let flatProp;
|
||||
let types = self.types;
|
||||
let type = self.properties[ prop.name ].type;
|
||||
let propIsBypass = prop.bypass;
|
||||
let origProp = style[ prop.name ];
|
||||
let origPropIsBypass = origProp && origProp.bypass;
|
||||
let _p = ele._private;
|
||||
let flatPropMapping = 'mapping';
|
||||
|
||||
let getVal = p => {
|
||||
if( p == null ){
|
||||
return null;
|
||||
} else if( p.pfValue != null ){
|
||||
return p.pfValue;
|
||||
} else {
|
||||
return p.value;
|
||||
}
|
||||
};
|
||||
|
||||
let checkTriggers = () => {
|
||||
let fromVal = getVal(origProp);
|
||||
let toVal = getVal(prop);
|
||||
|
||||
self.checkTriggers( ele, prop.name, fromVal, toVal );
|
||||
};
|
||||
|
||||
// edge sanity checks to prevent the client from making serious mistakes
|
||||
if(
|
||||
parsedProp.name === 'curve-style'
|
||||
&& ele.isEdge()
|
||||
&& (
|
||||
( // loops must be bundled beziers
|
||||
parsedProp.value !== 'bezier'
|
||||
&& ele.isLoop()
|
||||
) || ( // edges connected to compound nodes can not be haystacks
|
||||
parsedProp.value === 'haystack'
|
||||
&& ( ele.source().isParent() || ele.target().isParent() )
|
||||
)
|
||||
)
|
||||
){
|
||||
prop = parsedProp = this.parse( parsedProp.name, 'bezier', propIsBypass );
|
||||
}
|
||||
|
||||
if( prop.delete ){ // delete the property and use the default value on falsey value
|
||||
style[ prop.name ] = undefined;
|
||||
|
||||
checkTriggers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( prop.deleteBypassed ){ // delete the property that the
|
||||
if( !origProp ){
|
||||
checkTriggers();
|
||||
|
||||
return true; // can't delete if no prop
|
||||
|
||||
} else if( origProp.bypass ){ // delete bypassed
|
||||
origProp.bypassed = undefined;
|
||||
|
||||
checkTriggers();
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false; // we're unsuccessful deleting the bypassed
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to delete the current bypass
|
||||
if( prop.deleteBypass ){ // then this property is just here to indicate we need to delete
|
||||
if( !origProp ){
|
||||
checkTriggers();
|
||||
|
||||
return true; // property is already not defined
|
||||
|
||||
} else if( origProp.bypass ){ // then replace the bypass property with the original
|
||||
// because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
|
||||
style[ prop.name ] = origProp.bypassed;
|
||||
|
||||
checkTriggers();
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
return false; // we're unsuccessful deleting the bypass
|
||||
}
|
||||
}
|
||||
|
||||
let printMappingErr = function(){
|
||||
util.warn( 'Do not assign mappings to elements without corresponding data (i.e. ele `' + ele.id() + '` has no mapping for property `' + prop.name + '` with data field `' + prop.field + '`); try a `[' + prop.field + ']` selector to limit scope to elements with `' + prop.field + '` defined' );
|
||||
};
|
||||
|
||||
// put the property in the style objects
|
||||
switch( prop.mapped ){ // flatten the property if mapped
|
||||
case types.mapData: {
|
||||
// flatten the field (e.g. data.foo.bar)
|
||||
let fields = prop.field.split( '.' );
|
||||
let fieldVal = _p.data;
|
||||
|
||||
for( let i = 0; i < fields.length && fieldVal; i++ ){
|
||||
let field = fields[ i ];
|
||||
fieldVal = fieldVal[ field ];
|
||||
}
|
||||
|
||||
if( fieldVal == null ){
|
||||
printMappingErr();
|
||||
return false;
|
||||
}
|
||||
|
||||
let percent;
|
||||
if( !is.number( fieldVal ) ){ // then don't apply and fall back on the existing style
|
||||
util.warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
|
||||
return false;
|
||||
} else {
|
||||
let fieldWidth = prop.fieldMax - prop.fieldMin;
|
||||
|
||||
if( fieldWidth === 0 ){ // safety check -- not strictly necessary as no props of zero range should be passed here
|
||||
percent = 0;
|
||||
} else {
|
||||
percent = (fieldVal - prop.fieldMin) / fieldWidth;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure to bound percent value
|
||||
if( percent < 0 ){
|
||||
percent = 0;
|
||||
} else if( percent > 1 ){
|
||||
percent = 1;
|
||||
}
|
||||
|
||||
if( type.color ){
|
||||
let r1 = prop.valueMin[0];
|
||||
let r2 = prop.valueMax[0];
|
||||
let g1 = prop.valueMin[1];
|
||||
let g2 = prop.valueMax[1];
|
||||
let b1 = prop.valueMin[2];
|
||||
let b2 = prop.valueMax[2];
|
||||
let a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
|
||||
let a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
|
||||
|
||||
let clr = [
|
||||
Math.round( r1 + (r2 - r1) * percent ),
|
||||
Math.round( g1 + (g2 - g1) * percent ),
|
||||
Math.round( b1 + (b2 - b1) * percent ),
|
||||
Math.round( a1 + (a2 - a1) * percent )
|
||||
];
|
||||
|
||||
flatProp = { // colours are simple, so just create the flat property instead of expensive string parsing
|
||||
bypass: prop.bypass, // we're a bypass if the mapping property is a bypass
|
||||
name: prop.name,
|
||||
value: clr,
|
||||
strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
|
||||
};
|
||||
|
||||
} else if( type.number ){
|
||||
let calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
|
||||
flatProp = this.parse( prop.name, calcValue, prop.bypass, flatPropMapping );
|
||||
|
||||
} else {
|
||||
return false; // can only map to colours and numbers
|
||||
}
|
||||
|
||||
if( !flatProp ){ // if we can't flatten the property, then don't apply the property and fall back on the existing style
|
||||
printMappingErr();
|
||||
return false;
|
||||
}
|
||||
|
||||
flatProp.mapping = prop; // keep a reference to the mapping
|
||||
prop = flatProp; // the flattened (mapped) property is the one we want
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// direct mapping
|
||||
case types.data: {
|
||||
// flatten the field (e.g. data.foo.bar)
|
||||
let fields = prop.field.split( '.' );
|
||||
let fieldVal = _p.data;
|
||||
|
||||
for( let i = 0; i < fields.length && fieldVal; i++ ){
|
||||
let field = fields[ i ];
|
||||
fieldVal = fieldVal[ field ];
|
||||
}
|
||||
|
||||
if( fieldVal != null ){
|
||||
flatProp = this.parse( prop.name, fieldVal, prop.bypass, flatPropMapping );
|
||||
}
|
||||
|
||||
if( !flatProp ){ // if we can't flatten the property, then don't apply and fall back on the existing style
|
||||
printMappingErr();
|
||||
return false;
|
||||
}
|
||||
|
||||
flatProp.mapping = prop; // keep a reference to the mapping
|
||||
prop = flatProp; // the flattened (mapped) property is the one we want
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case types.fn: {
|
||||
let fn = prop.value;
|
||||
let fnRetVal = prop.fnValue != null ? prop.fnValue : fn( ele ); // check for cached value before calling function
|
||||
|
||||
prop.prevFnValue = fnRetVal;
|
||||
|
||||
if( fnRetVal == null ){
|
||||
util.warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
|
||||
return false;
|
||||
}
|
||||
|
||||
flatProp = this.parse( prop.name, fnRetVal, prop.bypass, flatPropMapping );
|
||||
|
||||
if( !flatProp ){
|
||||
util.warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
|
||||
return false;
|
||||
}
|
||||
|
||||
flatProp.mapping = util.copy( prop ); // keep a reference to the mapping
|
||||
prop = flatProp; // the flattened (mapped) property is the one we want
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case undefined:
|
||||
break; // just set the property
|
||||
|
||||
default:
|
||||
return false; // not a valid mapping
|
||||
}
|
||||
|
||||
// if the property is a bypass property, then link the resultant property to the original one
|
||||
if( propIsBypass ){
|
||||
if( origPropIsBypass ){ // then this bypass overrides the existing one
|
||||
prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
|
||||
} else { // then link the orig prop to the new bypass
|
||||
prop.bypassed = origProp;
|
||||
}
|
||||
|
||||
style[ prop.name ] = prop; // and set
|
||||
|
||||
} else { // prop is not bypass
|
||||
if( origPropIsBypass ){ // then keep the orig prop (since it's a bypass) and link to the new prop
|
||||
origProp.bypassed = prop;
|
||||
} else { // then just replace the old prop with the new one
|
||||
style[ prop.name ] = prop;
|
||||
}
|
||||
}
|
||||
|
||||
checkTriggers();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
styfn.cleanElements = function( eles, keepBypasses ){
|
||||
for( let i = 0; i < eles.length; i++ ){
|
||||
let ele = eles[i];
|
||||
|
||||
this.clearStyleHints(ele);
|
||||
|
||||
ele.dirtyCompoundBoundsCache();
|
||||
ele.dirtyBoundingBoxCache();
|
||||
|
||||
if( !keepBypasses ){
|
||||
ele._private.style = {};
|
||||
} else {
|
||||
let style = ele._private.style;
|
||||
let propNames = Object.keys(style);
|
||||
|
||||
for( let j = 0; j < propNames.length; j++ ){
|
||||
let propName = propNames[j];
|
||||
let eleProp = style[ propName ];
|
||||
|
||||
if( eleProp != null ){
|
||||
if( eleProp.bypass ){
|
||||
eleProp.bypassed = null;
|
||||
} else {
|
||||
style[ propName ] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// updates the visual style for all elements (useful for manual style modification after init)
|
||||
styfn.update = function(){
|
||||
let cy = this._private.cy;
|
||||
let eles = cy.mutableElements();
|
||||
|
||||
eles.updateStyle();
|
||||
};
|
||||
|
||||
// diffProps : { name => { prev, next } }
|
||||
styfn.updateTransitions = function( ele, diffProps ){
|
||||
let self = this;
|
||||
let _p = ele._private;
|
||||
let props = ele.pstyle( 'transition-property' ).value;
|
||||
let duration = ele.pstyle( 'transition-duration' ).pfValue;
|
||||
let delay = ele.pstyle( 'transition-delay' ).pfValue;
|
||||
|
||||
if( props.length > 0 && duration > 0 ){
|
||||
|
||||
let style = {};
|
||||
|
||||
// build up the style to animate towards
|
||||
let anyPrev = false;
|
||||
for( let i = 0; i < props.length; i++ ){
|
||||
let prop = props[ i ];
|
||||
let styProp = ele.pstyle( prop );
|
||||
let diffProp = diffProps[ prop ];
|
||||
|
||||
if( !diffProp ){ continue; }
|
||||
|
||||
let prevProp = diffProp.prev;
|
||||
let fromProp = prevProp;
|
||||
let toProp = diffProp.next != null ? diffProp.next : styProp;
|
||||
let diff = false;
|
||||
let initVal;
|
||||
let initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
|
||||
|
||||
if( !fromProp ){ continue; }
|
||||
|
||||
// consider px values
|
||||
if( is.number( fromProp.pfValue ) && is.number( toProp.pfValue ) ){
|
||||
diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
|
||||
initVal = fromProp.pfValue + initDt * diff;
|
||||
|
||||
// consider numerical values
|
||||
} else if( is.number( fromProp.value ) && is.number( toProp.value ) ){
|
||||
diff = toProp.value - fromProp.value; // nonzero is truthy
|
||||
initVal = fromProp.value + initDt * diff;
|
||||
|
||||
// consider colour values
|
||||
} else if( is.array( fromProp.value ) && is.array( toProp.value ) ){
|
||||
diff = fromProp.value[0] !== toProp.value[0]
|
||||
|| fromProp.value[1] !== toProp.value[1]
|
||||
|| fromProp.value[2] !== toProp.value[2]
|
||||
;
|
||||
|
||||
initVal = fromProp.strValue;
|
||||
}
|
||||
|
||||
// the previous value is good for an animation only if it's different
|
||||
if( diff ){
|
||||
style[ prop ] = toProp.strValue; // to val
|
||||
this.applyBypass( ele, prop, initVal ); // from val
|
||||
anyPrev = true;
|
||||
}
|
||||
|
||||
} // end if props allow ani
|
||||
|
||||
// can't transition if there's nothing previous to transition from
|
||||
if( !anyPrev ){ return; }
|
||||
|
||||
_p.transitioning = true;
|
||||
|
||||
( new Promise(function( resolve ){
|
||||
if( delay > 0 ){
|
||||
ele.delayAnimation( delay ).play().promise().then( resolve );
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}) ).then(function(){
|
||||
return ele.animation( {
|
||||
style: style,
|
||||
duration: duration,
|
||||
easing: ele.pstyle( 'transition-timing-function' ).value,
|
||||
queue: false
|
||||
} ).play().promise();
|
||||
}).then(function(){
|
||||
// if( !isBypass ){
|
||||
self.removeBypasses( ele, props );
|
||||
ele.emitAndNotify('style');
|
||||
// }
|
||||
|
||||
_p.transitioning = false;
|
||||
});
|
||||
|
||||
} else if( _p.transitioning ){
|
||||
this.removeBypasses( ele, props );
|
||||
ele.emitAndNotify('style');
|
||||
|
||||
_p.transitioning = false;
|
||||
}
|
||||
};
|
||||
|
||||
styfn.checkTrigger = function( ele, name, fromValue, toValue, getTrigger, onTrigger ){
|
||||
let prop = this.properties[ name ];
|
||||
let triggerCheck = getTrigger( prop );
|
||||
|
||||
if (ele.removed()) { return; }
|
||||
|
||||
if( triggerCheck != null && triggerCheck( fromValue, toValue, ele ) ){
|
||||
onTrigger(prop);
|
||||
}
|
||||
};
|
||||
|
||||
styfn.checkZOrderTrigger = function( ele, name, fromValue, toValue ){
|
||||
this.checkTrigger( ele, name, fromValue, toValue, prop => prop.triggersZOrder, () => {
|
||||
this._private.cy.notify('zorder', ele);
|
||||
});
|
||||
};
|
||||
|
||||
styfn.checkBoundsTrigger = function( ele, name, fromValue, toValue ){
|
||||
this.checkTrigger( ele, name, fromValue, toValue, prop => prop.triggersBounds, prop => {
|
||||
ele.dirtyCompoundBoundsCache();
|
||||
ele.dirtyBoundingBoxCache();
|
||||
});
|
||||
};
|
||||
|
||||
styfn.checkConnectedEdgesBoundsTrigger = function( ele, name, fromValue, toValue ){
|
||||
this.checkTrigger( ele, name, fromValue, toValue, prop => prop.triggersBoundsOfConnectedEdges, prop => {
|
||||
ele.connectedEdges().forEach(edge => {
|
||||
edge.dirtyBoundingBoxCache();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
styfn.checkParallelEdgesBoundsTrigger = function( ele, name, fromValue, toValue ){
|
||||
this.checkTrigger( ele, name, fromValue, toValue, prop => prop.triggersBoundsOfParallelEdges, prop => {
|
||||
ele.parallelEdges().forEach(pllEdge => {
|
||||
pllEdge.dirtyBoundingBoxCache();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
styfn.checkTriggers = function( ele, name, fromValue, toValue ){
|
||||
ele.dirtyStyleCache();
|
||||
|
||||
this.checkZOrderTrigger( ele, name, fromValue, toValue );
|
||||
this.checkBoundsTrigger( ele, name, fromValue, toValue );
|
||||
this.checkConnectedEdgesBoundsTrigger( ele, name, fromValue, toValue );
|
||||
this.checkParallelEdgesBoundsTrigger( ele, name, fromValue, toValue );
|
||||
};
|
||||
|
||||
export default styfn;
|
||||
204
frontend/node_modules/cytoscape/src/style/index.mjs
generated
vendored
Normal file
204
frontend/node_modules/cytoscape/src/style/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
import * as is from '../is.mjs';
|
||||
import * as util from '../util/index.mjs';
|
||||
import Selector from '../selector/index.mjs';
|
||||
|
||||
import apply from './apply.mjs';
|
||||
import bypass from './bypass.mjs';
|
||||
import container from './container.mjs';
|
||||
import getForEle from './get-for-ele.mjs';
|
||||
import json from './json.mjs';
|
||||
import stringSheet from './string-sheet.mjs';
|
||||
import properties from './properties.mjs';
|
||||
import parse from './parse.mjs';
|
||||
|
||||
let Style = function( cy ){
|
||||
|
||||
if( !(this instanceof Style) ){
|
||||
return new Style( cy );
|
||||
}
|
||||
|
||||
if( !is.core( cy ) ){
|
||||
util.error( 'A style must have a core reference' );
|
||||
return;
|
||||
}
|
||||
|
||||
this._private = {
|
||||
cy: cy,
|
||||
coreStyle: {}
|
||||
};
|
||||
|
||||
this.length = 0;
|
||||
|
||||
this.resetToDefault();
|
||||
};
|
||||
|
||||
let styfn = Style.prototype;
|
||||
|
||||
styfn.instanceString = function(){
|
||||
return 'style';
|
||||
};
|
||||
|
||||
// remove all contexts
|
||||
styfn.clear = function(){
|
||||
let _p = this._private;
|
||||
let cy = _p.cy;
|
||||
let eles = cy.elements();
|
||||
|
||||
for( let i = 0; i < this.length; i++ ){
|
||||
this[ i ] = undefined;
|
||||
}
|
||||
this.length = 0;
|
||||
|
||||
_p.contextStyles = {};
|
||||
_p.propDiffs = {};
|
||||
|
||||
this.cleanElements( eles, true );
|
||||
|
||||
eles.forEach(ele => {
|
||||
let ele_p = ele[0]._private;
|
||||
|
||||
ele_p.styleDirty = true;
|
||||
ele_p.appliedInitStyle = false;
|
||||
});
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
styfn.resetToDefault = function(){
|
||||
this.clear();
|
||||
this.addDefaultStylesheet();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// builds a style object for the 'core' selector
|
||||
styfn.core = function( propName ){
|
||||
return this._private.coreStyle[ propName ] || this.getDefaultProperty( propName );
|
||||
};
|
||||
|
||||
// create a new context from the specified selector string and switch to that context
|
||||
styfn.selector = function( selectorStr ){
|
||||
// 'core' is a special case and does not need a selector
|
||||
let selector = selectorStr === 'core' ? null : new Selector( selectorStr );
|
||||
|
||||
let i = this.length++; // new context means new index
|
||||
this[ i ] = {
|
||||
selector: selector,
|
||||
properties: [],
|
||||
mappedProperties: [],
|
||||
index: i
|
||||
};
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
// add one or many css rules to the current context
|
||||
styfn.css = function(){
|
||||
let self = this;
|
||||
let args = arguments;
|
||||
|
||||
if( args.length === 1 ){
|
||||
let map = args[0];
|
||||
|
||||
for( let i = 0; i < self.properties.length; i++ ){
|
||||
let prop = self.properties[ i ];
|
||||
let mapVal = map[ prop.name ];
|
||||
|
||||
if( mapVal === undefined ){
|
||||
mapVal = map[ util.dash2camel( prop.name ) ];
|
||||
}
|
||||
|
||||
if( mapVal !== undefined ){
|
||||
this.cssRule( prop.name, mapVal );
|
||||
}
|
||||
}
|
||||
|
||||
} else if( args.length === 2 ){
|
||||
this.cssRule( args[0], args[1] );
|
||||
}
|
||||
|
||||
// do nothing if args are invalid
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
styfn.style = styfn.css;
|
||||
|
||||
// add a single css rule to the current context
|
||||
styfn.cssRule = function( name, value ){
|
||||
// name-value pair
|
||||
let property = this.parse( name, value );
|
||||
|
||||
// add property to current context if valid
|
||||
if( property ){
|
||||
let i = this.length - 1;
|
||||
this[ i ].properties.push( property );
|
||||
this[ i ].properties[ property.name ] = property; // allow access by name as well
|
||||
|
||||
if( property.name.match( /pie-(\d+)-background-size/ ) && property.value ){
|
||||
this._private.hasPie = true;
|
||||
}
|
||||
|
||||
if( property.name.match( /stripe-(\d+)-background-size/ ) && property.value ){
|
||||
this._private.hasStripe = true;
|
||||
}
|
||||
|
||||
if( property.mapped ){
|
||||
this[ i ].mappedProperties.push( property );
|
||||
}
|
||||
|
||||
// add to core style if necessary
|
||||
let currentSelectorIsCore = !this[ i ].selector;
|
||||
if( currentSelectorIsCore ){
|
||||
this._private.coreStyle[ property.name ] = property;
|
||||
}
|
||||
}
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
styfn.append = function( style ){
|
||||
if( is.stylesheet( style ) ){
|
||||
style.appendToStyle( this );
|
||||
} else if( is.array( style ) ){
|
||||
this.appendFromJson( style );
|
||||
} else if( is.string( style ) ){
|
||||
this.appendFromString( style );
|
||||
} // you probably wouldn't want to append a Style, since you'd duplicate the default parts
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// static function
|
||||
Style.fromJson = function( cy, json ){
|
||||
let style = new Style( cy );
|
||||
|
||||
style.fromJson( json );
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
Style.fromString = function( cy, string ){
|
||||
return new Style( cy ).fromString( string );
|
||||
};
|
||||
|
||||
[
|
||||
apply,
|
||||
bypass,
|
||||
container,
|
||||
getForEle,
|
||||
json,
|
||||
stringSheet,
|
||||
properties,
|
||||
parse
|
||||
].forEach( function( props ){
|
||||
util.extend( styfn, props );
|
||||
} );
|
||||
|
||||
|
||||
Style.types = styfn.types;
|
||||
Style.properties = styfn.properties;
|
||||
Style.propertyGroups = styfn.propertyGroups;
|
||||
Style.propertyGroupNames = styfn.propertyGroupNames;
|
||||
Style.propertyGroupKeys = styfn.propertyGroupKeys;
|
||||
|
||||
export default Style;
|
||||
59
frontend/node_modules/cytoscape/src/style/json.mjs
generated
vendored
Normal file
59
frontend/node_modules/cytoscape/src/style/json.mjs
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
let styfn = {};
|
||||
|
||||
styfn.appendFromJson = function( json ){
|
||||
let style = this;
|
||||
|
||||
for( let i = 0; i < json.length; i++ ){
|
||||
let context = json[ i ];
|
||||
let selector = context.selector;
|
||||
let props = context.style || context.css;
|
||||
let names = Object.keys( props );
|
||||
|
||||
style.selector( selector ); // apply selector
|
||||
|
||||
for( let j = 0; j < names.length; j++ ){
|
||||
let name = names[j];
|
||||
let value = props[ name ];
|
||||
|
||||
style.css( name, value ); // apply property
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
// accessible cy.style() function
|
||||
styfn.fromJson = function( json ){
|
||||
let style = this;
|
||||
|
||||
style.resetToDefault();
|
||||
style.appendFromJson( json );
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
// get json from cy.style() api
|
||||
styfn.json = function(){
|
||||
let json = [];
|
||||
|
||||
for( let i = this.defaultLength; i < this.length; i++ ){
|
||||
let cxt = this[ i ];
|
||||
let selector = cxt.selector;
|
||||
let props = cxt.properties;
|
||||
let css = {};
|
||||
|
||||
for( let j = 0; j < props.length; j++ ){
|
||||
let prop = props[ j ];
|
||||
css[ prop.name ] = prop.strValue;
|
||||
}
|
||||
|
||||
json.push( {
|
||||
selector: !selector ? 'core' : selector.toString(),
|
||||
style: css
|
||||
} );
|
||||
}
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
export default styfn;
|
||||
435
frontend/node_modules/cytoscape/src/style/parse.mjs
generated
vendored
Normal file
435
frontend/node_modules/cytoscape/src/style/parse.mjs
generated
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
import * as util from '../util/index.mjs';
|
||||
import * as is from '../is.mjs';
|
||||
import * as math from '../math.mjs';
|
||||
|
||||
let styfn = {};
|
||||
|
||||
// a caching layer for property parsing
|
||||
styfn.parse = function( name, value, propIsBypass, propIsFlat ){
|
||||
let self = this;
|
||||
|
||||
// function values can't be cached in all cases, and there isn't much benefit of caching them anyway
|
||||
if( is.fn( value ) ){
|
||||
return self.parseImplWarn( name, value, propIsBypass, propIsFlat );
|
||||
}
|
||||
|
||||
let flatKey = ( propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ) ? 'dontcare' : propIsFlat;
|
||||
let bypassKey = propIsBypass ? 't' : 'f';
|
||||
let valueKey = '' + value;
|
||||
let argHash = util.hashStrings( name, valueKey, bypassKey, flatKey );
|
||||
let propCache = self.propCache = self.propCache || [];
|
||||
let ret;
|
||||
|
||||
if( !(ret = propCache[ argHash ]) ){
|
||||
ret = propCache[ argHash ] = self.parseImplWarn( name, value, propIsBypass, propIsFlat );
|
||||
}
|
||||
|
||||
// - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
|
||||
// - mappings can't be shared b/c mappings are per-element
|
||||
if( propIsBypass || propIsFlat === 'mapping' ){
|
||||
// need a copy since props are mutated later in their lifecycles
|
||||
ret = util.copy( ret );
|
||||
|
||||
if( ret ){
|
||||
ret.value = util.copy( ret.value ); // because it could be an array, e.g. colour
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
styfn.parseImplWarn = function( name, value, propIsBypass, propIsFlat ){
|
||||
let prop = this.parseImpl( name, value, propIsBypass, propIsFlat );
|
||||
|
||||
if( !prop && value != null ){
|
||||
util.warn(`The style property \`${name}: ${value}\` is invalid`);
|
||||
}
|
||||
|
||||
if( prop && (prop.name === 'width' || prop.name === 'height') && value === 'label' ){
|
||||
util.warn('The style value of `label` is deprecated for `' + prop.name + '`');
|
||||
}
|
||||
|
||||
return prop;
|
||||
};
|
||||
|
||||
// parse a property; return null on invalid; return parsed property otherwise
|
||||
// fields :
|
||||
// - name : the name of the property
|
||||
// - value : the parsed, native-typed value of the property
|
||||
// - strValue : a string value that represents the property value in valid css
|
||||
// - bypass : true iff the property is a bypass property
|
||||
styfn.parseImpl = function( name, value, propIsBypass, propIsFlat ){
|
||||
let self = this;
|
||||
|
||||
name = util.camel2dash( name ); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
|
||||
|
||||
let property = self.properties[ name ];
|
||||
let passedValue = value;
|
||||
let types = self.types;
|
||||
|
||||
if( !property ){ return null; } // return null on property of unknown name
|
||||
if( value === undefined ){ return null; } // can't assign undefined
|
||||
|
||||
// the property may be an alias
|
||||
if( property.alias ){
|
||||
property = property.pointsTo;
|
||||
name = property.name;
|
||||
}
|
||||
|
||||
let valueIsString = is.string( value );
|
||||
if( valueIsString ){ // trim the value to make parsing easier
|
||||
value = value.trim();
|
||||
}
|
||||
|
||||
let type = property.type;
|
||||
if( !type ){ return null; } // no type, no luck
|
||||
|
||||
// check if bypass is null or empty string (i.e. indication to delete bypass property)
|
||||
if( propIsBypass && (value === '' || value === null) ){
|
||||
return {
|
||||
name: name,
|
||||
value: value,
|
||||
bypass: true,
|
||||
deleteBypass: true
|
||||
};
|
||||
}
|
||||
|
||||
// check if value is a function used as a mapper
|
||||
if( is.fn( value ) ){
|
||||
return {
|
||||
name: name,
|
||||
value: value,
|
||||
strValue: 'fn',
|
||||
mapped: types.fn,
|
||||
bypass: propIsBypass
|
||||
};
|
||||
}
|
||||
|
||||
// check if value is mapped
|
||||
let data, mapData;
|
||||
if( !valueIsString || propIsFlat || value.length < 7 || value[1] !== 'a' ){
|
||||
// then don't bother to do the expensive regex checks
|
||||
|
||||
} else if(value.length >= 7 && value[0] === 'd' && ( data = new RegExp( types.data.regex ).exec( value ) )){
|
||||
if( propIsBypass ){ return false; } // mappers not allowed in bypass
|
||||
|
||||
let mapped = types.data;
|
||||
|
||||
return {
|
||||
name: name,
|
||||
value: data,
|
||||
strValue: '' + value,
|
||||
mapped: mapped,
|
||||
field: data[1],
|
||||
bypass: propIsBypass
|
||||
};
|
||||
|
||||
} else if(value.length >= 10 && value[0] === 'm' && ( mapData = new RegExp( types.mapData.regex ).exec( value ) )){
|
||||
if( propIsBypass ){ return false; } // mappers not allowed in bypass
|
||||
if( type.multiple ){ return false; } // impossible to map to num
|
||||
|
||||
let mapped = types.mapData;
|
||||
|
||||
// we can map only if the type is a colour or a number
|
||||
if( !(type.color || type.number) ){ return false; }
|
||||
|
||||
let valueMin = this.parse( name, mapData[4] ); // parse to validate
|
||||
if( !valueMin || valueMin.mapped ){ return false; } // can't be invalid or mapped
|
||||
|
||||
let valueMax = this.parse( name, mapData[5] ); // parse to validate
|
||||
if( !valueMax || valueMax.mapped ){ return false; } // can't be invalid or mapped
|
||||
|
||||
// check if valueMin and valueMax are the same
|
||||
if( valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue ){
|
||||
util.warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
|
||||
|
||||
return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
|
||||
|
||||
} else if( type.color ){
|
||||
let c1 = valueMin.value;
|
||||
let c2 = valueMax.value;
|
||||
|
||||
let same = c1[0] === c2[0] // red
|
||||
&& c1[1] === c2[1] // green
|
||||
&& c1[2] === c2[2] // blue
|
||||
&& ( // optional alpha
|
||||
c1[3] === c2[3] // same alpha outright
|
||||
|| (
|
||||
(c1[3] == null || c1[3] === 1) // full opacity for colour 1?
|
||||
&&
|
||||
(c2[3] == null || c2[3] === 1) // full opacity for colour 2?
|
||||
)
|
||||
)
|
||||
;
|
||||
|
||||
if( same ){ return false; } // can't make a mapper without a range
|
||||
}
|
||||
|
||||
return {
|
||||
name: name,
|
||||
value: mapData,
|
||||
strValue: '' + value,
|
||||
mapped: mapped,
|
||||
field: mapData[1],
|
||||
fieldMin: parseFloat( mapData[2] ), // min & max are numeric
|
||||
fieldMax: parseFloat( mapData[3] ),
|
||||
valueMin: valueMin.value,
|
||||
valueMax: valueMax.value,
|
||||
bypass: propIsBypass
|
||||
};
|
||||
}
|
||||
|
||||
if( type.multiple && propIsFlat !== 'multiple' ){
|
||||
let vals;
|
||||
|
||||
if( valueIsString ){
|
||||
vals = value.split( /\s+/ );
|
||||
} else if( is.array( value ) ){
|
||||
vals = value;
|
||||
} else {
|
||||
vals = [ value ];
|
||||
}
|
||||
|
||||
if( type.evenMultiple && vals.length % 2 !== 0 ){ return null; }
|
||||
|
||||
let valArr = [];
|
||||
let unitsArr = [];
|
||||
let pfValArr = [];
|
||||
let strVal = '';
|
||||
let hasEnum = false;
|
||||
|
||||
for( let i = 0; i < vals.length; i++ ){
|
||||
let p = self.parse( name, vals[i], propIsBypass, 'multiple' );
|
||||
|
||||
hasEnum = hasEnum || is.string( p.value );
|
||||
|
||||
valArr.push( p.value );
|
||||
pfValArr.push( p.pfValue != null ? p.pfValue : p.value );
|
||||
unitsArr.push( p.units );
|
||||
strVal += (i > 0 ? ' ' : '') + p.strValue;
|
||||
}
|
||||
|
||||
if( type.validate && !type.validate( valArr, unitsArr ) ){
|
||||
return null;
|
||||
}
|
||||
|
||||
if( type.singleEnum && hasEnum ){
|
||||
if( valArr.length === 1 && is.string( valArr[0] ) ){
|
||||
return {
|
||||
name: name,
|
||||
value: valArr[0],
|
||||
strValue: valArr[0],
|
||||
bypass: propIsBypass
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: name,
|
||||
value: valArr,
|
||||
pfValue: pfValArr,
|
||||
strValue: strVal,
|
||||
bypass: propIsBypass,
|
||||
units: unitsArr
|
||||
};
|
||||
}
|
||||
|
||||
// several types also allow enums
|
||||
let checkEnums = function(){
|
||||
for( let i = 0; i < type.enums.length; i++ ){
|
||||
let en = type.enums[ i ];
|
||||
|
||||
if( en === value ){
|
||||
return {
|
||||
name: name,
|
||||
value: value,
|
||||
strValue: '' + value,
|
||||
bypass: propIsBypass
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// check the type and return the appropriate object
|
||||
if( type.number ){
|
||||
let units;
|
||||
let implicitUnits = 'px'; // not set => px
|
||||
|
||||
if( type.units ){ // use specified units if set
|
||||
units = type.units;
|
||||
}
|
||||
|
||||
if( type.implicitUnits ){
|
||||
implicitUnits = type.implicitUnits;
|
||||
}
|
||||
|
||||
if( !type.unitless ){
|
||||
if( valueIsString ){
|
||||
let unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
|
||||
if( units ){ unitsRegex = units; } // only allow explicit units if so set
|
||||
let match = value.match( '^(' + util.regex.number + ')(' + unitsRegex + ')?' + '$' );
|
||||
|
||||
if( match ){
|
||||
value = match[1];
|
||||
units = match[2] || implicitUnits;
|
||||
}
|
||||
|
||||
} else if( !units || type.implicitUnits ){
|
||||
units = implicitUnits; // implicitly px if unspecified
|
||||
}
|
||||
}
|
||||
|
||||
value = parseFloat( value );
|
||||
|
||||
// if not a number and enums not allowed, then the value is invalid
|
||||
if( isNaN( value ) && type.enums === undefined ){
|
||||
return null;
|
||||
}
|
||||
|
||||
// check if this number type also accepts special keywords in place of numbers
|
||||
// (i.e. `left`, `auto`, etc)
|
||||
if( isNaN( value ) && type.enums !== undefined ){
|
||||
value = passedValue;
|
||||
|
||||
return checkEnums();
|
||||
}
|
||||
|
||||
// check if value must be an integer
|
||||
if( type.integer && !is.integer( value ) ){
|
||||
return null;
|
||||
}
|
||||
|
||||
// check value is within range
|
||||
if( ( type.min !== undefined && ( value < type.min || (type.strictMin && value === type.min) ) )
|
||||
|| ( type.max !== undefined && ( value > type.max || (type.strictMax && value === type.max) ) )
|
||||
){
|
||||
return null;
|
||||
}
|
||||
|
||||
let ret = {
|
||||
name: name,
|
||||
value: value,
|
||||
strValue: '' + value + (units ? units : ''),
|
||||
units: units,
|
||||
bypass: propIsBypass
|
||||
};
|
||||
|
||||
// normalise value in pixels
|
||||
if( type.unitless || (units !== 'px' && units !== 'em') ){
|
||||
ret.pfValue = value;
|
||||
} else {
|
||||
ret.pfValue = ( units === 'px' || !units ? (value) : (this.getEmSizeInPixels() * value) );
|
||||
}
|
||||
|
||||
// normalise value in ms
|
||||
if( units === 'ms' || units === 's' ){
|
||||
ret.pfValue = units === 'ms' ? value : 1000 * value;
|
||||
}
|
||||
|
||||
// normalise value in rad
|
||||
if( units === 'deg' || units === 'rad' ){
|
||||
ret.pfValue = units === 'rad' ? value : math.deg2rad( value );
|
||||
}
|
||||
|
||||
// normalize value in %
|
||||
if( units === '%' ){
|
||||
ret.pfValue = value / 100;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
} else if( type.propList ){
|
||||
|
||||
let props = [];
|
||||
let propsStr = '' + value;
|
||||
|
||||
if( propsStr === 'none' ){
|
||||
// leave empty
|
||||
|
||||
} else { // go over each prop
|
||||
|
||||
let propsSplit = propsStr.split( /\s*,\s*|\s+/ );
|
||||
for( let i = 0; i < propsSplit.length; i++ ){
|
||||
let propName = propsSplit[ i ].trim();
|
||||
|
||||
if( self.properties[ propName ] ){
|
||||
props.push( propName );
|
||||
} else {
|
||||
util.warn('`' + propName + '` is not a valid property name');
|
||||
}
|
||||
}
|
||||
|
||||
if( props.length === 0 ){ return null; }
|
||||
}
|
||||
|
||||
return {
|
||||
name: name,
|
||||
value: props,
|
||||
strValue: props.length === 0 ? 'none' : props.join(' '),
|
||||
bypass: propIsBypass
|
||||
};
|
||||
|
||||
} else if( type.color ){
|
||||
let tuple = util.color2tuple( value );
|
||||
|
||||
if( !tuple ){ return null; }
|
||||
|
||||
return {
|
||||
name: name,
|
||||
value: tuple,
|
||||
pfValue: tuple,
|
||||
strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')', // n.b. no spaces b/c of multiple support
|
||||
bypass: propIsBypass
|
||||
};
|
||||
|
||||
} else if( type.regex || type.regexes ){
|
||||
|
||||
// first check enums
|
||||
if( type.enums ){
|
||||
let enumProp = checkEnums();
|
||||
|
||||
if( enumProp ){ return enumProp; }
|
||||
}
|
||||
|
||||
let regexes = type.regexes ? type.regexes : [ type.regex ];
|
||||
|
||||
for( let i = 0; i < regexes.length; i++ ){
|
||||
let regex = new RegExp( regexes[ i ] ); // make a regex from the type string
|
||||
let m = regex.exec( value );
|
||||
|
||||
if( m ){ // regex matches
|
||||
return {
|
||||
name: name,
|
||||
value: type.singleRegexMatchValue ? m[1] : m,
|
||||
strValue: '' + value,
|
||||
bypass: propIsBypass
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return null; // didn't match any
|
||||
|
||||
} else if( type.string ){
|
||||
// just return
|
||||
return {
|
||||
name: name,
|
||||
value: '' + value,
|
||||
strValue: '' + value,
|
||||
bypass: propIsBypass
|
||||
};
|
||||
|
||||
} else if( type.enums ){ // check enums last because it's a combo type in others
|
||||
return checkEnums();
|
||||
|
||||
} else {
|
||||
return null; // not a type we can handle
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default styfn;
|
||||
97
frontend/node_modules/cytoscape/src/stylesheet.mjs
generated
vendored
Normal file
97
frontend/node_modules/cytoscape/src/stylesheet.mjs
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
import * as is from './is.mjs';
|
||||
import Style from './style/index.mjs';
|
||||
import { dash2camel } from './util/index.mjs';
|
||||
|
||||
// a dummy stylesheet object that doesn't need a reference to the core
|
||||
// (useful for init)
|
||||
let Stylesheet = function(){
|
||||
if( !(this instanceof Stylesheet) ){
|
||||
return new Stylesheet();
|
||||
}
|
||||
|
||||
this.length = 0;
|
||||
};
|
||||
|
||||
let sheetfn = Stylesheet.prototype;
|
||||
|
||||
sheetfn.instanceString = function(){
|
||||
return 'stylesheet';
|
||||
};
|
||||
|
||||
// just store the selector to be parsed later
|
||||
sheetfn.selector = function( selector ){
|
||||
let i = this.length++;
|
||||
|
||||
this[ i ] = {
|
||||
selector: selector,
|
||||
properties: []
|
||||
};
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
// just store the property to be parsed later
|
||||
sheetfn.css = function( name, value ){
|
||||
let i = this.length - 1;
|
||||
|
||||
if( is.string( name ) ){
|
||||
this[ i ].properties.push( {
|
||||
name: name,
|
||||
value: value
|
||||
} );
|
||||
} else if( is.plainObject( name ) ){
|
||||
let map = name;
|
||||
let propNames = Object.keys( map );
|
||||
|
||||
for( let j = 0; j < propNames.length; j++ ){
|
||||
let key = propNames[ j ];
|
||||
let mapVal = map[ key ];
|
||||
|
||||
if( mapVal == null ){ continue; }
|
||||
|
||||
let prop = Style.properties[key] || Style.properties[dash2camel(key)];
|
||||
|
||||
if( prop == null ){ continue; }
|
||||
|
||||
let name = prop.name;
|
||||
let value = mapVal;
|
||||
|
||||
this[ i ].properties.push( {
|
||||
name: name,
|
||||
value: value
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
return this; // chaining
|
||||
};
|
||||
|
||||
sheetfn.style = sheetfn.css;
|
||||
|
||||
// generate a real style object from the dummy stylesheet
|
||||
sheetfn.generateStyle = function( cy ){
|
||||
let style = new Style( cy );
|
||||
|
||||
return this.appendToStyle( style );
|
||||
};
|
||||
|
||||
// append a dummy stylesheet object on a real style object
|
||||
sheetfn.appendToStyle = function( style ){
|
||||
for( let i = 0; i < this.length; i++ ){
|
||||
let context = this[ i ];
|
||||
let selector = context.selector;
|
||||
let props = context.properties;
|
||||
|
||||
style.selector( selector ); // apply selector
|
||||
|
||||
for( let j = 0; j < props.length; j++ ){
|
||||
let prop = props[ j ];
|
||||
|
||||
style.css( prop.name, prop.value ); // apply property
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
};
|
||||
|
||||
export default Stylesheet;
|
||||
18
frontend/node_modules/cytoscape/src/test.mjs
generated
vendored
Normal file
18
frontend/node_modules/cytoscape/src/test.mjs
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
This file tells the Mocha tests what build of Cytoscape to use.
|
||||
*/
|
||||
|
||||
// For manual build tests, use the ESM build
|
||||
// NB : Must do `npm run build` before `npm test`
|
||||
let cytoscape;
|
||||
|
||||
if (process.env.TEST_BUILD) {
|
||||
// Dynamically import the ESM build
|
||||
cytoscape = await import('../build/cytoscape.esm.mjs'); // Assuming the ESM build uses `.esm.js`
|
||||
} else {
|
||||
// Dynamically import the unbundled, unbabelified raw source
|
||||
cytoscape = await import('./index.mjs');
|
||||
}
|
||||
|
||||
// Export the module (adjust based on whether you need default or named exports)
|
||||
export default cytoscape.default || cytoscape;
|
||||
290
frontend/node_modules/cytoscape/src/util/colors.mjs
generated
vendored
Normal file
290
frontend/node_modules/cytoscape/src/util/colors.mjs
generated
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
import * as is from '../is.mjs';
|
||||
import * as regex from './regex.mjs';
|
||||
|
||||
// get [r, g, b] from #abc or #aabbcc
|
||||
export const hex2tuple = hex => {
|
||||
if( !(hex.length === 4 || hex.length === 7) || hex[0] !== '#' ){ return; }
|
||||
|
||||
let shortHex = hex.length === 4;
|
||||
let r, g, b;
|
||||
let base = 16;
|
||||
|
||||
if( shortHex ){
|
||||
r = parseInt( hex[1] + hex[1], base );
|
||||
g = parseInt( hex[2] + hex[2], base );
|
||||
b = parseInt( hex[3] + hex[3], base );
|
||||
} else {
|
||||
r = parseInt( hex[1] + hex[2], base );
|
||||
g = parseInt( hex[3] + hex[4], base );
|
||||
b = parseInt( hex[5] + hex[6], base );
|
||||
}
|
||||
|
||||
return [ r, g, b ];
|
||||
};
|
||||
|
||||
// get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
|
||||
export const hsl2tuple = hsl => {
|
||||
let ret;
|
||||
let h, s, l, a, r, g, b;
|
||||
function hue2rgb( p, q, t ){
|
||||
if( t < 0 ) t += 1;
|
||||
if( t > 1 ) t -= 1;
|
||||
if( t < 1 / 6 ) return p + (q - p) * 6 * t;
|
||||
if( t < 1 / 2 ) return q;
|
||||
if( t < 2 / 3 ) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
let m = new RegExp( '^' + regex.hsla + '$' ).exec( hsl );
|
||||
if( m ){
|
||||
|
||||
// get hue
|
||||
h = parseInt( m[1] );
|
||||
if( h < 0 ){
|
||||
h = ( 360 - (-1 * h % 360) ) % 360;
|
||||
} else if( h > 360 ){
|
||||
h = h % 360;
|
||||
}
|
||||
h /= 360; // normalise on [0, 1]
|
||||
|
||||
s = parseFloat( m[2] );
|
||||
if( s < 0 || s > 100 ){ return; } // saturation is [0, 100]
|
||||
s = s / 100; // normalise on [0, 1]
|
||||
|
||||
l = parseFloat( m[3] );
|
||||
if( l < 0 || l > 100 ){ return; } // lightness is [0, 100]
|
||||
l = l / 100; // normalise on [0, 1]
|
||||
|
||||
a = m[4];
|
||||
if( a !== undefined ){
|
||||
a = parseFloat( a );
|
||||
|
||||
if( a < 0 || a > 1 ){ return; } // alpha is [0, 1]
|
||||
}
|
||||
|
||||
// now, convert to rgb
|
||||
// code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
|
||||
if( s === 0 ){
|
||||
r = g = b = Math.round( l * 255 ); // achromatic
|
||||
} else {
|
||||
let q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
let p = 2 * l - q;
|
||||
r = Math.round( 255 * hue2rgb( p, q, h + 1 / 3 ) );
|
||||
g = Math.round( 255 * hue2rgb( p, q, h ) );
|
||||
b = Math.round( 255 * hue2rgb( p, q, h - 1 / 3 ) );
|
||||
}
|
||||
|
||||
ret = [ r, g, b, a ];
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
// get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
|
||||
export const rgb2tuple = rgb => {
|
||||
let ret;
|
||||
|
||||
let m = new RegExp( '^' + regex.rgba + '$' ).exec( rgb );
|
||||
if( m ){
|
||||
ret = [];
|
||||
|
||||
let isPct = [];
|
||||
for( let i = 1; i <= 3; i++ ){
|
||||
let channel = m[ i ];
|
||||
|
||||
if( channel[ channel.length - 1 ] === '%' ){
|
||||
isPct[ i ] = true;
|
||||
}
|
||||
channel = parseFloat( channel );
|
||||
|
||||
if( isPct[ i ] ){
|
||||
channel = channel / 100 * 255; // normalise to [0, 255]
|
||||
}
|
||||
|
||||
if( channel < 0 || channel > 255 ){ return; } // invalid channel value
|
||||
|
||||
ret.push( Math.floor( channel ) );
|
||||
}
|
||||
|
||||
let atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
|
||||
let allArePct = isPct[1] && isPct[2] && isPct[3];
|
||||
if( atLeastOneIsPct && !allArePct ){ return; } // must all be percent values if one is
|
||||
|
||||
let alpha = m[4];
|
||||
if( alpha !== undefined ){
|
||||
alpha = parseFloat( alpha );
|
||||
|
||||
if( alpha < 0 || alpha > 1 ){ return; } // invalid alpha value
|
||||
|
||||
ret.push( alpha );
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
export const colorname2tuple = color => {
|
||||
return colors[ color.toLowerCase() ];
|
||||
};
|
||||
|
||||
export const color2tuple = color => {
|
||||
return ( is.array( color ) ? color : null )
|
||||
|| colorname2tuple( color )
|
||||
|| hex2tuple( color )
|
||||
|| rgb2tuple( color )
|
||||
|| hsl2tuple( color );
|
||||
};
|
||||
|
||||
export const colors = {
|
||||
// special colour names
|
||||
transparent: [0, 0, 0, 0], // NB alpha === 0
|
||||
|
||||
// regular colours
|
||||
aliceblue: [ 240, 248, 255 ],
|
||||
antiquewhite: [ 250, 235, 215 ],
|
||||
aqua: [0, 255, 255 ],
|
||||
aquamarine: [ 127, 255, 212 ],
|
||||
azure: [ 240, 255, 255 ],
|
||||
beige: [ 245, 245, 220 ],
|
||||
bisque: [ 255, 228, 196 ],
|
||||
black: [0, 0, 0],
|
||||
blanchedalmond: [ 255, 235, 205 ],
|
||||
blue: [0, 0, 255 ],
|
||||
blueviolet: [ 138, 43, 226 ],
|
||||
brown: [ 165, 42, 42 ],
|
||||
burlywood: [ 222, 184, 135 ],
|
||||
cadetblue: [ 95, 158, 160 ],
|
||||
chartreuse: [ 127, 255, 0],
|
||||
chocolate: [ 210, 105, 30 ],
|
||||
coral: [ 255, 127, 80 ],
|
||||
cornflowerblue: [ 100, 149, 237 ],
|
||||
cornsilk: [ 255, 248, 220 ],
|
||||
crimson: [ 220, 20, 60 ],
|
||||
cyan: [0, 255, 255 ],
|
||||
darkblue: [0, 0, 139 ],
|
||||
darkcyan: [0, 139, 139 ],
|
||||
darkgoldenrod: [ 184, 134, 11 ],
|
||||
darkgray: [ 169, 169, 169 ],
|
||||
darkgreen: [0, 100, 0],
|
||||
darkgrey: [ 169, 169, 169 ],
|
||||
darkkhaki: [ 189, 183, 107 ],
|
||||
darkmagenta: [ 139, 0, 139 ],
|
||||
darkolivegreen: [ 85, 107, 47 ],
|
||||
darkorange: [ 255, 140, 0],
|
||||
darkorchid: [ 153, 50, 204 ],
|
||||
darkred: [ 139, 0, 0],
|
||||
darksalmon: [ 233, 150, 122 ],
|
||||
darkseagreen: [ 143, 188, 143 ],
|
||||
darkslateblue: [ 72, 61, 139 ],
|
||||
darkslategray: [ 47, 79, 79 ],
|
||||
darkslategrey: [ 47, 79, 79 ],
|
||||
darkturquoise: [0, 206, 209 ],
|
||||
darkviolet: [ 148, 0, 211 ],
|
||||
deeppink: [ 255, 20, 147 ],
|
||||
deepskyblue: [0, 191, 255 ],
|
||||
dimgray: [ 105, 105, 105 ],
|
||||
dimgrey: [ 105, 105, 105 ],
|
||||
dodgerblue: [ 30, 144, 255 ],
|
||||
firebrick: [ 178, 34, 34 ],
|
||||
floralwhite: [ 255, 250, 240 ],
|
||||
forestgreen: [ 34, 139, 34 ],
|
||||
fuchsia: [ 255, 0, 255 ],
|
||||
gainsboro: [ 220, 220, 220 ],
|
||||
ghostwhite: [ 248, 248, 255 ],
|
||||
gold: [ 255, 215, 0],
|
||||
goldenrod: [ 218, 165, 32 ],
|
||||
gray: [ 128, 128, 128 ],
|
||||
grey: [ 128, 128, 128 ],
|
||||
green: [0, 128, 0],
|
||||
greenyellow: [ 173, 255, 47 ],
|
||||
honeydew: [ 240, 255, 240 ],
|
||||
hotpink: [ 255, 105, 180 ],
|
||||
indianred: [ 205, 92, 92 ],
|
||||
indigo: [ 75, 0, 130 ],
|
||||
ivory: [ 255, 255, 240 ],
|
||||
khaki: [ 240, 230, 140 ],
|
||||
lavender: [ 230, 230, 250 ],
|
||||
lavenderblush: [ 255, 240, 245 ],
|
||||
lawngreen: [ 124, 252, 0],
|
||||
lemonchiffon: [ 255, 250, 205 ],
|
||||
lightblue: [ 173, 216, 230 ],
|
||||
lightcoral: [ 240, 128, 128 ],
|
||||
lightcyan: [ 224, 255, 255 ],
|
||||
lightgoldenrodyellow: [ 250, 250, 210 ],
|
||||
lightgray: [ 211, 211, 211 ],
|
||||
lightgreen: [ 144, 238, 144 ],
|
||||
lightgrey: [ 211, 211, 211 ],
|
||||
lightpink: [ 255, 182, 193 ],
|
||||
lightsalmon: [ 255, 160, 122 ],
|
||||
lightseagreen: [ 32, 178, 170 ],
|
||||
lightskyblue: [ 135, 206, 250 ],
|
||||
lightslategray: [ 119, 136, 153 ],
|
||||
lightslategrey: [ 119, 136, 153 ],
|
||||
lightsteelblue: [ 176, 196, 222 ],
|
||||
lightyellow: [ 255, 255, 224 ],
|
||||
lime: [0, 255, 0],
|
||||
limegreen: [ 50, 205, 50 ],
|
||||
linen: [ 250, 240, 230 ],
|
||||
magenta: [ 255, 0, 255 ],
|
||||
maroon: [ 128, 0, 0],
|
||||
mediumaquamarine: [ 102, 205, 170 ],
|
||||
mediumblue: [0, 0, 205 ],
|
||||
mediumorchid: [ 186, 85, 211 ],
|
||||
mediumpurple: [ 147, 112, 219 ],
|
||||
mediumseagreen: [ 60, 179, 113 ],
|
||||
mediumslateblue: [ 123, 104, 238 ],
|
||||
mediumspringgreen: [0, 250, 154 ],
|
||||
mediumturquoise: [ 72, 209, 204 ],
|
||||
mediumvioletred: [ 199, 21, 133 ],
|
||||
midnightblue: [ 25, 25, 112 ],
|
||||
mintcream: [ 245, 255, 250 ],
|
||||
mistyrose: [ 255, 228, 225 ],
|
||||
moccasin: [ 255, 228, 181 ],
|
||||
navajowhite: [ 255, 222, 173 ],
|
||||
navy: [0, 0, 128 ],
|
||||
oldlace: [ 253, 245, 230 ],
|
||||
olive: [ 128, 128, 0],
|
||||
olivedrab: [ 107, 142, 35 ],
|
||||
orange: [ 255, 165, 0],
|
||||
orangered: [ 255, 69, 0],
|
||||
orchid: [ 218, 112, 214 ],
|
||||
palegoldenrod: [ 238, 232, 170 ],
|
||||
palegreen: [ 152, 251, 152 ],
|
||||
paleturquoise: [ 175, 238, 238 ],
|
||||
palevioletred: [ 219, 112, 147 ],
|
||||
papayawhip: [ 255, 239, 213 ],
|
||||
peachpuff: [ 255, 218, 185 ],
|
||||
peru: [ 205, 133, 63 ],
|
||||
pink: [ 255, 192, 203 ],
|
||||
plum: [ 221, 160, 221 ],
|
||||
powderblue: [ 176, 224, 230 ],
|
||||
purple: [ 128, 0, 128 ],
|
||||
red: [ 255, 0, 0],
|
||||
rosybrown: [ 188, 143, 143 ],
|
||||
royalblue: [ 65, 105, 225 ],
|
||||
saddlebrown: [ 139, 69, 19 ],
|
||||
salmon: [ 250, 128, 114 ],
|
||||
sandybrown: [ 244, 164, 96 ],
|
||||
seagreen: [ 46, 139, 87 ],
|
||||
seashell: [ 255, 245, 238 ],
|
||||
sienna: [ 160, 82, 45 ],
|
||||
silver: [ 192, 192, 192 ],
|
||||
skyblue: [ 135, 206, 235 ],
|
||||
slateblue: [ 106, 90, 205 ],
|
||||
slategray: [ 112, 128, 144 ],
|
||||
slategrey: [ 112, 128, 144 ],
|
||||
snow: [ 255, 250, 250 ],
|
||||
springgreen: [0, 255, 127 ],
|
||||
steelblue: [ 70, 130, 180 ],
|
||||
tan: [ 210, 180, 140 ],
|
||||
teal: [0, 128, 128 ],
|
||||
thistle: [ 216, 191, 216 ],
|
||||
tomato: [ 255, 99, 71 ],
|
||||
turquoise: [ 64, 224, 208 ],
|
||||
violet: [ 238, 130, 238 ],
|
||||
wheat: [ 245, 222, 179 ],
|
||||
white: [ 255, 255, 255 ],
|
||||
whitesmoke: [ 245, 245, 245 ],
|
||||
yellow: [ 255, 255, 0],
|
||||
yellowgreen: [ 154, 205, 50 ]
|
||||
};
|
||||
Reference in New Issue
Block a user