├── .github └── workflows │ └── build.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── copyright-header.txt ├── example ├── editor_contents.js ├── example.css ├── example.js └── index.html ├── package-lock.json ├── package.json ├── rollup.config.js ├── scripts ├── build-css.js └── enhance-types.js ├── src ├── css │ └── ace-collab-ext.css └── ts │ ├── AceCursorMarker.ts │ ├── AceMultiCursorManager.ts │ ├── AceMultiSelectionManager.ts │ ├── AceRadarView.ts │ ├── AceRadarViewIndicator.ts │ ├── AceRangeUtil.ts │ ├── AceSelectionMarker.ts │ ├── AceViewportUtil.ts │ ├── IndexRange.ts │ ├── RowRange.ts │ └── index.ts ├── tsconfig.json └── tslint.json /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Build 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [14.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v2 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run dist 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | dist 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v0.6.0](https://github.com/convergencelabs/ace-collab-ext/tree/0.6.0) (2021-07-10) 4 | 5 | **Enhancements:** 6 | - Moved from webpack to rollup. 7 | - Removed gulp in favor of a straight node build. 8 | - Migrated from Travis CI to GitHub Actions. 9 | 10 | 11 | ## [v0.5.0](https://github.com/convergencelabs/ace-collab-ext/tree/0.5.0) (2020-03-14) 12 | 13 | **Enhancements:** 14 | - Moves from the `@convergence` to `@convergencelabs` scope. 15 | 16 | **Fixes:** 17 | - [\#15](https://github.com/convergencelabs/ace-collab-ext/issues/15) Fixed bad module import in the AceRadarView. 18 | 19 | ## [v0.4.0](https://github.com/convergencelabs/ace-collab-ext/tree/0.4.0) (2019-03-15) 20 | 21 | **Enhancements:** 22 | - [\#7](https://github.com/convergencelabs/ace-collab-ext/issues/7) Migrate from `brace` to `ace-builds`. 23 | 24 | 25 | ## [v0.3.0](https://github.com/convergencelabs/ace-collab-ext/tree/0.3.0) (2019-01-23) 26 | 27 | **Enhancements:** 28 | - [\#5](https://github.com/convergencelabs/ace-collab-ext/issues/5) Add cursor user tooltips. 29 | - [\#6](https://github.com/convergencelabs/ace-collab-ext/issues/6) Update to work with Ace > 1.4.0. 30 | 31 | 32 | ## [v0.2.3](https://github.com/convergencelabs/ace-collab-ext/tree/0.2.3) (2018-11-13) 33 | 34 | **Fixes:** 35 | 36 | - [\#4](https://github.com/convergencelabs/ace-collab-ext/pull/4) Improved selection when line wrapping is enabled. 37 | 38 | 39 | ## [v0.2.2](https://github.com/convergencelabs/ace-collab-ext/tree/0.2.2) (2018-05-01) 40 | 41 | **Enhancements:** 42 | 43 | - Added the RadarView.clearView method. 44 | 45 | 46 | ## [v0.2.1](https://github.com/convergencelabs/ace-collab-ext/tree/0.2.1) (2018-05-01) 47 | 48 | **Enhancements:** 49 | 50 | - Improved typings to better support UMD imports. 51 | - Updated the Multi Cursor Manager to accept Positions in addition to Indices 52 | 53 | 54 | ## [v0.2.0](https://github.com/convergencelabs/ace-collab-ext/tree/0.2.0) (2018-05-01) 55 | 56 | **Enhancements:** 57 | 58 | - [\#2](https://github.com/convergencelabs/ace-collab-ext/issues/2) The library now contains typescript definitions. 59 | - [\#3](https://github.com/convergencelabs/ace-collab-ext/issues/3) The common js build is no longer webpacked. 60 | 61 | 62 | ## [v0.1.1](https://github.com/convergencelabs/ace-collab-ext/tree/0.1.1) (2017-01-10) 63 | 64 | **Enhancements:** 65 | 66 | - [\#1](https://github.com/convergencelabs/ace-collab-ext/issues/1) The AceMultiCursorManager and AceMultiSelectionManager constructors now take an Ace EditSession instance instead of the Ace Editor instance so they can be initialized with a session before the editor is constructed. 67 | 68 | 69 | ## [v0.1.0](https://github.com/convergencelabs/ace-collab-ext/tree/0.1.0) (2016-12-27) 70 | 71 | - Initial release. 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Convergence Labs, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 17 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 18 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Ace Collaborative Extensions 2 | [![example workflow](https://github.com/convergencelabs/ace-collab-ext/actions/workflows/build.yml/badge.svg)](https://github.com/convergencelabs/ace-collab-ext/actions/workflows/build.yml) 3 | 4 | Enhances the [Ace Editor](https://github.com/ajaxorg/ace) by adding the ability to render cues about what remote users are doing in the system. 5 | 6 | ## Installation 7 | 8 | Install package with NPM and add it to your development dependencies: 9 | 10 | For versions >= 0.5.0 (current): 11 | ```npm install --save-dev @convergencelabs/ace-collab-ext``` 12 | 13 | For versions <= 0.4.0 (previous): 14 | ```npm install --save-dev @convergence/ace-collab-ext``` 15 | 16 | ## Demo 17 | Go [here](https://examples.convergence.io/examples/ace/) to see a live demo of multiple cursors, multiple selections, and remote scrollbars (Visit on multiple browsers, or even better, point a friend to it too). This uses [Convergence](https://convergence.io) to handle the synchronization of data and user actions. 18 | 19 | ## Usage 20 | 21 | ### CSS 22 | Be sure to include one of CSS files located in the css directory of the node modules: 23 | 24 | * `css/ace-collab-ext.css` 25 | * `css/ace-collab-ext.min.css` 26 | 27 | How to do this will depend on how you are packaging and distributing your application. For example if you are bundling your css / sass / less you might be able to use an `@import` statement or you might `require` it. If you are hotlinking, you might need to at a `` tag to your document. 28 | 29 | If you forget to include the styles, its likely that the remote cursors / selections will either not show up, or they will not properly move. 30 | 31 | ### Multi Cursor Manager 32 | The multi cursor manager allows you to easily render the cursors of other users 33 | working in the same document. The cursor position can be represented as either 34 | a single linear index or as a 2-dimensional position in the form of 35 | ```{row: 0, column: 10}```. 36 | 37 | ```javascript 38 | const editor = ace.edit("editor"); 39 | const curMgr = new AceCollabExt.AceMultiCursorManager(editor.getSession()); 40 | 41 | // Add a new remote cursor with an id of "uid1", and a color of orange. 42 | curMgr.addCursor("uid1", "User 1", "orange", {row: 0, column: 10}); 43 | 44 | // Set cursor for "uid1" to index 10. 45 | curMgr.setCursor("uid1", 10); 46 | 47 | // Clear the remote cursor for "uid1" without removing it. 48 | curMgr.clearCursor("uid1"); 49 | 50 | // Remove the remote cursor for "uid1". 51 | curMgr.removeCursor("uid1"); 52 | ``` 53 | 54 | ### Multi Selection Manager 55 | The multi selection manager allows you to easily render the selection of other 56 | users working in the same document. Selection is represented by an array of 57 | AceRanges. A single range is common for normal selection, but multiple ranges 58 | are needed to support block selection. 59 | 60 | ```javascript 61 | const AceRange = ace.require("ace/range"); 62 | 63 | const editor = ace.edit("editor"); 64 | const selMgr = new AceCollabExt.AceMultiSelectionManager(editor.getSession()); 65 | 66 | // A two-line block selection 67 | const initialRanges = [ 68 | new AceRange(0, 0, 0, 10), 69 | new AceRange(1, 0, 1, 10), 70 | ]; 71 | 72 | // Add a new remote view indicator with an id of "uid1", and a color of orange. 73 | selMgr.addSelection("uid1", "User 1", "orange", initialRanges); 74 | 75 | // Set the selection to a new range. 76 | selMgr.setSelection("uid1", new AceRange(10, 0, 11, 10)); 77 | 78 | // Nullify the selection without removing the marker. 79 | selMgr.clearSelection("uid1"); 80 | 81 | // Remove the remote view indicator for "uid1". 82 | selMgr.removeSelection("uid1"); 83 | ``` 84 | 85 | ### Radar View 86 | A radar view indicates where in a document another user is looking and allows 87 | you to easily go to the location in the document. 88 | 89 | ```javascript 90 | const editor = ace.edit("editor"); 91 | const radarView = new AceCollabExt.RadarView("radarView", editor); 92 | 93 | // Add a new remote view indicator with an id of "uid1", and a color of orange. 94 | radarView.addView("uid1", "user1", "orange", 0, 20, 0); 95 | 96 | // Set the viewport range of the indicator to span rows 10 through 40. 97 | radarView.setViewRows("uid1", 10, 40); 98 | 99 | // Set the row location of the cursor to line 10. 100 | radarView.setCursorRow("uid1", 10); 101 | 102 | // Remove the remote view indicator for "uid1". 103 | radarView.removeView("uid1"); 104 | ``` 105 | -------------------------------------------------------------------------------- /copyright-header.txt: -------------------------------------------------------------------------------- 1 | © 2016-2021 Convergence Labs, Inc. 2 | @version <%= pkg.version %> 3 | @license MIT -------------------------------------------------------------------------------- /example/editor_contents.js: -------------------------------------------------------------------------------- 1 | var editorContents = `var observableProto; 2 | 3 | /** 4 | * Represents a push-style collection. 5 | */ 6 | var Observable = Rx.Observable = (function () { 7 | 8 | function makeSubscribe(self, subscribe) { 9 | return function (o) { 10 | var oldOnError = o.onError; 11 | o.onError = function (e) { 12 | makeStackTraceLong(e, self); 13 | oldOnError.call(o, e); 14 | }; 15 | 16 | return subscribe.call(self, o); 17 | }; 18 | } 19 | 20 | function Observable() { 21 | if (Rx.config.longStackSupport && hasStacks) { 22 | var oldSubscribe = this._subscribe; 23 | var e = tryCatch(thrower)(new Error()).e; 24 | this.stack = e.stack.substring(e.stack.indexOf('\\n') + 1); 25 | this._subscribe = makeSubscribe(this, oldSubscribe); 26 | } 27 | } 28 | 29 | observableProto = Observable.prototype; 30 | 31 | /** 32 | * Determines whether the given object is an Observable 33 | * @param {Any} An object to determine whether it is an Observable 34 | * @returns {Boolean} true if an Observable, else false. 35 | */ 36 | Observable.isObservable = function (o) { 37 | return o && isFunction(o.subscribe); 38 | }; 39 | 40 | /** 41 | * Subscribes an o to the observable sequence. 42 | * @param {Mixed} [oOrOnNext] The object that is to receive notifications or an action to invoke for each element in the observable sequence. 43 | * @param {Function} [onError] Action to invoke upon exceptional termination of the observable sequence. 44 | * @param {Function} [onCompleted] Action to invoke upon graceful termination of the observable sequence. 45 | * @returns {Diposable} A disposable handling the subscriptions and unsubscriptions. 46 | */ 47 | observableProto.subscribe = observableProto.forEach = function (oOrOnNext, onError, onCompleted) { 48 | return this._subscribe(typeof oOrOnNext === 'object' ? 49 | oOrOnNext : 50 | observerCreate(oOrOnNext, onError, onCompleted)); 51 | }; 52 | 53 | /** 54 | * Subscribes to the next value in the sequence with an optional "this" argument. 55 | * @param {Function} onNext The function to invoke on each element in the observable sequence. 56 | * @param {Any} [thisArg] Object to use as this when executing callback. 57 | * @returns {Disposable} A disposable handling the subscriptions and unsubscriptions. 58 | */ 59 | observableProto.subscribeOnNext = function (onNext, thisArg) { 60 | return this._subscribe(observerCreate(typeof thisArg !== 'undefined' ? function(x) { onNext.call(thisArg, x); } : onNext)); 61 | }; 62 | 63 | /** 64 | * Subscribes to an exceptional condition in the sequence with an optional "this" argument. 65 | * @param {Function} onError The function to invoke upon exceptional termination of the observable sequence. 66 | * @param {Any} [thisArg] Object to use as this when executing callback. 67 | * @returns {Disposable} A disposable handling the subscriptions and unsubscriptions. 68 | */ 69 | observableProto.subscribeOnError = function (onError, thisArg) { 70 | return this._subscribe(observerCreate(null, typeof thisArg !== 'undefined' ? function(e) { onError.call(thisArg, e); } : onError)); 71 | }; 72 | 73 | /** 74 | * Subscribes to the next value in the sequence with an optional "this" argument. 75 | * @param {Function} onCompleted The function to invoke upon graceful termination of the observable sequence. 76 | * @param {Any} [thisArg] Object to use as this when executing callback. 77 | * @returns {Disposable} A disposable handling the subscriptions and unsubscriptions. 78 | */ 79 | observableProto.subscribeOnCompleted = function (onCompleted, thisArg) { 80 | return this._subscribe(observerCreate(null, null, typeof thisArg !== 'undefined' ? function() { onCompleted.call(thisArg); } : onCompleted)); 81 | }; 82 | 83 | return Observable; 84 | })();`; -------------------------------------------------------------------------------- /example/example.css: -------------------------------------------------------------------------------- 1 | .body { 2 | margin: 0; 3 | } 4 | 5 | .main-container { 6 | position: absolute; 7 | top: 0; 8 | bottom: 0; 9 | left: 0; 10 | right: 0; 11 | display: flex; 12 | flex-direction: column; 13 | padding: 5px; 14 | } 15 | 16 | .title { 17 | font-family: "Helvetica Neue", sans-serif; 18 | font-size: 18px; 19 | font-weight: bold; 20 | margin-bottom: 10px; 21 | } 22 | 23 | .editors { 24 | display: flex; 25 | flex-direction: row; 26 | flex: 1; 27 | } 28 | 29 | .editor-column { 30 | display: flex; 31 | flex-direction: column; 32 | flex: 1; 33 | } 34 | 35 | .editor-label { 36 | text-align: center; 37 | font-family: "Helvetica Neue", sans-serif; 38 | font-size: 14px; 39 | font-weight: bold; 40 | margin-bottom: 10px; 41 | } 42 | 43 | .editor { 44 | display: inline-block; 45 | margin-left: 10px; 46 | flex: 1; 47 | } 48 | 49 | .wrapped-editor { 50 | flex: 1; 51 | display: flex; 52 | } 53 | 54 | #target-radar-view { 55 | display: inline-block; 56 | min-width: 20px; 57 | background: #2F3129; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | const sourceUser = { 2 | id: "source", 3 | label: "Source User", 4 | color: "orange" 5 | }; 6 | 7 | const sourceEditor = initEditor("source-editor"); 8 | const sourceSession = sourceEditor.getSession(); 9 | 10 | const targetEditor = initEditor("target-editor"); 11 | targetEditor.setReadOnly(true); 12 | 13 | const targetCursorManager = new AceCollabExt.AceMultiCursorManager(targetEditor.getSession()); 14 | targetCursorManager.addCursor(sourceUser.id, sourceUser.label, sourceUser.color, 0); 15 | 16 | const targetSelectionManager = new AceCollabExt.AceMultiSelectionManager(targetEditor.getSession()); 17 | targetSelectionManager.addSelection(sourceUser.id, sourceUser.label, sourceUser.color, []); 18 | 19 | const radarView = new AceCollabExt.AceRadarView("target-radar-view", targetEditor); 20 | 21 | 22 | setTimeout(function() { 23 | radarView.addView("fake1", "fake1", "RoyalBlue", {start: 60, end: 75}, 50); 24 | radarView.addView("fake2", "fake2", "lightgreen", {start: 10, end: 50}, 30); 25 | 26 | const initialIndices = AceCollabExt.AceViewportUtil.getVisibleIndexRange(sourceEditor); 27 | const initialRows = AceCollabExt.AceViewportUtil.indicesToRows(sourceEditor, initialIndices.start, initialIndices.end); 28 | radarView.addView(sourceUser.id, sourceUser.label, sourceUser.color, initialRows, 0); 29 | }, 0); 30 | 31 | sourceSession.getDocument().on("change", function(e) { 32 | targetEditor.getSession().getDocument().applyDeltas([e]); 33 | }); 34 | 35 | sourceSession.on("changeScrollTop", function (scrollTop) { 36 | setTimeout(function () { 37 | const viewportIndices = AceCollabExt.AceViewportUtil.getVisibleIndexRange(sourceEditor); 38 | const rows = AceCollabExt.AceViewportUtil.indicesToRows(sourceEditor, viewportIndices.start, viewportIndices.end); 39 | radarView.setViewRows(sourceUser.id, rows); 40 | }, 0); 41 | }); 42 | 43 | sourceSession.selection.on('changeCursor', function(e) { 44 | const cursor = sourceEditor.getCursorPosition(); 45 | targetCursorManager.setCursor(sourceUser.id, cursor); 46 | radarView.setCursorRow(sourceUser.id, cursor.row); 47 | }); 48 | 49 | sourceSession.selection.on('changeSelection', function(e) { 50 | const rangesJson = AceCollabExt.AceRangeUtil.toJson(sourceEditor.selection.getAllRanges()); 51 | const ranges = AceCollabExt.AceRangeUtil.fromJson(rangesJson); 52 | targetSelectionManager.setSelection(sourceUser.id, ranges); 53 | }); 54 | 55 | function initEditor(id) { 56 | const editor = ace.edit(id); 57 | editor.setTheme('ace/theme/monokai'); 58 | 59 | const session = editor.getSession(); 60 | session.setMode('ace/mode/javascript'); 61 | session.setValue(editorContents); 62 | 63 | return editor; 64 | } 65 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ACE Collaborative Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
Ace Collaborative Example
17 |
18 |
19 |
Source Editor
20 |
21 |
22 |
23 |
Target Editor
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@convergencelabs/ace-collab-ext", 3 | "version": "0.6.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@convergencelabs/ace-collab-ext", 9 | "version": "0.6.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "ace-builds": "^1.4.12" 13 | }, 14 | "devDependencies": { 15 | "@rollup/plugin-commonjs": "19.0.0", 16 | "@rollup/plugin-node-resolve": "13.0.0", 17 | "@rollup/plugin-typescript": "8.2.1", 18 | "@types/backbone": "1.4.1", 19 | "clean-css": "^5.1.3", 20 | "fs-extra": "^10.0.0", 21 | "rimraf": "^3.0.2", 22 | "rollup": "2.47.0", 23 | "rollup-plugin-license": "2.3.0", 24 | "rollup-plugin-terser": "7.0.2", 25 | "tslib": "^2.3.0", 26 | "typescript": "4.2.4" 27 | } 28 | }, 29 | "node_modules/@babel/code-frame": { 30 | "version": "7.14.5", 31 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", 32 | "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", 33 | "dev": true, 34 | "dependencies": { 35 | "@babel/highlight": "^7.14.5" 36 | }, 37 | "engines": { 38 | "node": ">=6.9.0" 39 | } 40 | }, 41 | "node_modules/@babel/helper-validator-identifier": { 42 | "version": "7.14.5", 43 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", 44 | "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", 45 | "dev": true, 46 | "engines": { 47 | "node": ">=6.9.0" 48 | } 49 | }, 50 | "node_modules/@babel/highlight": { 51 | "version": "7.14.5", 52 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", 53 | "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", 54 | "dev": true, 55 | "dependencies": { 56 | "@babel/helper-validator-identifier": "^7.14.5", 57 | "chalk": "^2.0.0", 58 | "js-tokens": "^4.0.0" 59 | }, 60 | "engines": { 61 | "node": ">=6.9.0" 62 | } 63 | }, 64 | "node_modules/@rollup/plugin-commonjs": { 65 | "version": "19.0.0", 66 | "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.0.tgz", 67 | "integrity": "sha512-adTpD6ATGbehdaQoZQ6ipDFhdjqsTgpOAhFiPwl+dzre4pPshsecptDPyEFb61JMJ1+mGljktaC4jI8ARMSNyw==", 68 | "dev": true, 69 | "dependencies": { 70 | "@rollup/pluginutils": "^3.1.0", 71 | "commondir": "^1.0.1", 72 | "estree-walker": "^2.0.1", 73 | "glob": "^7.1.6", 74 | "is-reference": "^1.2.1", 75 | "magic-string": "^0.25.7", 76 | "resolve": "^1.17.0" 77 | }, 78 | "engines": { 79 | "node": ">= 8.0.0" 80 | }, 81 | "peerDependencies": { 82 | "rollup": "^2.38.3" 83 | } 84 | }, 85 | "node_modules/@rollup/plugin-node-resolve": { 86 | "version": "13.0.0", 87 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.0.tgz", 88 | "integrity": "sha512-41X411HJ3oikIDivT5OKe9EZ6ud6DXudtfNrGbC4nniaxx2esiWjkLOzgnZsWq1IM8YIeL2rzRGLZLBjlhnZtQ==", 89 | "dev": true, 90 | "dependencies": { 91 | "@rollup/pluginutils": "^3.1.0", 92 | "@types/resolve": "1.17.1", 93 | "builtin-modules": "^3.1.0", 94 | "deepmerge": "^4.2.2", 95 | "is-module": "^1.0.0", 96 | "resolve": "^1.19.0" 97 | }, 98 | "engines": { 99 | "node": ">= 10.0.0" 100 | }, 101 | "peerDependencies": { 102 | "rollup": "^2.42.0" 103 | } 104 | }, 105 | "node_modules/@rollup/plugin-node-resolve/node_modules/builtin-modules": { 106 | "version": "3.2.0", 107 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", 108 | "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", 109 | "dev": true, 110 | "engines": { 111 | "node": ">=6" 112 | }, 113 | "funding": { 114 | "url": "https://github.com/sponsors/sindresorhus" 115 | } 116 | }, 117 | "node_modules/@rollup/plugin-typescript": { 118 | "version": "8.2.1", 119 | "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.1.tgz", 120 | "integrity": "sha512-Qd2E1pleDR4bwyFxqbjt4eJf+wB0UKVMLc7/BAFDGVdAXQMCsD4DUv5/7/ww47BZCYxWtJqe1Lo0KVNswBJlRw==", 121 | "dev": true, 122 | "dependencies": { 123 | "@rollup/pluginutils": "^3.1.0", 124 | "resolve": "^1.17.0" 125 | }, 126 | "engines": { 127 | "node": ">=8.0.0" 128 | }, 129 | "peerDependencies": { 130 | "rollup": "^2.14.0", 131 | "tslib": "*", 132 | "typescript": ">=3.7.0" 133 | } 134 | }, 135 | "node_modules/@rollup/pluginutils": { 136 | "version": "3.1.0", 137 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", 138 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", 139 | "dev": true, 140 | "dependencies": { 141 | "@types/estree": "0.0.39", 142 | "estree-walker": "^1.0.1", 143 | "picomatch": "^2.2.2" 144 | }, 145 | "engines": { 146 | "node": ">= 8.0.0" 147 | }, 148 | "peerDependencies": { 149 | "rollup": "^1.20.0||^2.0.0" 150 | } 151 | }, 152 | "node_modules/@rollup/pluginutils/node_modules/estree-walker": { 153 | "version": "1.0.1", 154 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", 155 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", 156 | "dev": true 157 | }, 158 | "node_modules/@types/backbone": { 159 | "version": "1.4.1", 160 | "resolved": "https://registry.npmjs.org/@types/backbone/-/backbone-1.4.1.tgz", 161 | "integrity": "sha512-KYfGuQy4d2vvYXbn0uHFZ6brFLndatTMomxBlljpbWf4kFpA3BG/6LA3ec+J9iredrX6eAVI7sm9SVAvwiIM6g==", 162 | "dev": true, 163 | "dependencies": { 164 | "@types/jquery": "*", 165 | "@types/underscore": "*" 166 | } 167 | }, 168 | "node_modules/@types/estree": { 169 | "version": "0.0.39", 170 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", 171 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", 172 | "dev": true 173 | }, 174 | "node_modules/@types/jquery": { 175 | "version": "3.5.6", 176 | "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.6.tgz", 177 | "integrity": "sha512-SmgCQRzGPId4MZQKDj9Hqc6kSXFNWZFHpELkyK8AQhf8Zr6HKfCzFv9ZC1Fv3FyQttJZOlap3qYb12h61iZAIg==", 178 | "dev": true, 179 | "dependencies": { 180 | "@types/sizzle": "*" 181 | } 182 | }, 183 | "node_modules/@types/node": { 184 | "version": "13.9.1", 185 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.1.tgz", 186 | "integrity": "sha512-E6M6N0blf/jiZx8Q3nb0vNaswQeEyn0XlupO+xN6DtJ6r6IT4nXrTry7zhIfYvFCl3/8Cu6WIysmUBKiqV0bqQ==", 187 | "dev": true 188 | }, 189 | "node_modules/@types/resolve": { 190 | "version": "1.17.1", 191 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", 192 | "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", 193 | "dev": true, 194 | "dependencies": { 195 | "@types/node": "*" 196 | } 197 | }, 198 | "node_modules/@types/sizzle": { 199 | "version": "2.3.3", 200 | "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", 201 | "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", 202 | "dev": true 203 | }, 204 | "node_modules/@types/underscore": { 205 | "version": "1.11.3", 206 | "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.3.tgz", 207 | "integrity": "sha512-Fl1TX1dapfXyDqFg2ic9M+vlXRktcPJrc4PR7sRc7sdVrjavg/JHlbUXBt8qWWqhJrmSqg3RNAkAPRiOYw6Ahw==", 208 | "dev": true 209 | }, 210 | "node_modules/ace-builds": { 211 | "version": "1.4.12", 212 | "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.12.tgz", 213 | "integrity": "sha512-G+chJctFPiiLGvs3+/Mly3apXTcfgE45dT5yp12BcWZ1kUs+gm0qd3/fv4gsz6fVag4mM0moHVpjHDIgph6Psg==" 214 | }, 215 | "node_modules/ansi-styles": { 216 | "version": "3.2.1", 217 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 218 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 219 | "dev": true, 220 | "dependencies": { 221 | "color-convert": "^1.9.0" 222 | }, 223 | "engines": { 224 | "node": ">=4" 225 | } 226 | }, 227 | "node_modules/array-find-index": { 228 | "version": "1.0.2", 229 | "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", 230 | "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", 231 | "dev": true, 232 | "engines": { 233 | "node": ">=0.10.0" 234 | } 235 | }, 236 | "node_modules/balanced-match": { 237 | "version": "1.0.0", 238 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 239 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 240 | "dev": true 241 | }, 242 | "node_modules/brace-expansion": { 243 | "version": "1.1.11", 244 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 245 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 246 | "dev": true, 247 | "dependencies": { 248 | "balanced-match": "^1.0.0", 249 | "concat-map": "0.0.1" 250 | } 251 | }, 252 | "node_modules/buffer-from": { 253 | "version": "1.1.1", 254 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 255 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 256 | "dev": true 257 | }, 258 | "node_modules/chalk": { 259 | "version": "2.4.2", 260 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 261 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 262 | "dev": true, 263 | "dependencies": { 264 | "ansi-styles": "^3.2.1", 265 | "escape-string-regexp": "^1.0.5", 266 | "supports-color": "^5.3.0" 267 | }, 268 | "engines": { 269 | "node": ">=4" 270 | } 271 | }, 272 | "node_modules/clean-css": { 273 | "version": "5.1.3", 274 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.1.3.tgz", 275 | "integrity": "sha512-qGXzUCDpLwAlPx0kYeU4QXjzQIcIYZbJjD4FNm7NnSjoP0hYMVZhHOpUYJ6AwfkMX2cceLRq54MeCgHy/va1cA==", 276 | "dev": true, 277 | "dependencies": { 278 | "source-map": "~0.6.0" 279 | }, 280 | "engines": { 281 | "node": ">= 10.0" 282 | } 283 | }, 284 | "node_modules/color-convert": { 285 | "version": "1.9.3", 286 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 287 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 288 | "dev": true, 289 | "dependencies": { 290 | "color-name": "1.1.3" 291 | } 292 | }, 293 | "node_modules/color-name": { 294 | "version": "1.1.3", 295 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 296 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 297 | "dev": true 298 | }, 299 | "node_modules/commenting": { 300 | "version": "1.1.0", 301 | "resolved": "https://registry.npmjs.org/commenting/-/commenting-1.1.0.tgz", 302 | "integrity": "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==", 303 | "dev": true 304 | }, 305 | "node_modules/commondir": { 306 | "version": "1.0.1", 307 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 308 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", 309 | "dev": true 310 | }, 311 | "node_modules/concat-map": { 312 | "version": "0.0.1", 313 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 314 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 315 | "dev": true 316 | }, 317 | "node_modules/deepmerge": { 318 | "version": "4.2.2", 319 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", 320 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", 321 | "dev": true, 322 | "engines": { 323 | "node": ">=0.10.0" 324 | } 325 | }, 326 | "node_modules/escape-string-regexp": { 327 | "version": "1.0.5", 328 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 329 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 330 | "dev": true, 331 | "engines": { 332 | "node": ">=0.8.0" 333 | } 334 | }, 335 | "node_modules/estree-walker": { 336 | "version": "2.0.2", 337 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 338 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 339 | "dev": true 340 | }, 341 | "node_modules/fs-extra": { 342 | "version": "10.0.0", 343 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", 344 | "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", 345 | "dev": true, 346 | "dependencies": { 347 | "graceful-fs": "^4.2.0", 348 | "jsonfile": "^6.0.1", 349 | "universalify": "^2.0.0" 350 | }, 351 | "engines": { 352 | "node": ">=12" 353 | } 354 | }, 355 | "node_modules/fs.realpath": { 356 | "version": "1.0.0", 357 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 358 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 359 | "dev": true 360 | }, 361 | "node_modules/function-bind": { 362 | "version": "1.1.1", 363 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 364 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 365 | "dev": true 366 | }, 367 | "node_modules/glob": { 368 | "version": "7.1.6", 369 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 370 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 371 | "dev": true, 372 | "dependencies": { 373 | "fs.realpath": "^1.0.0", 374 | "inflight": "^1.0.4", 375 | "inherits": "2", 376 | "minimatch": "^3.0.4", 377 | "once": "^1.3.0", 378 | "path-is-absolute": "^1.0.0" 379 | }, 380 | "engines": { 381 | "node": "*" 382 | }, 383 | "funding": { 384 | "url": "https://github.com/sponsors/isaacs" 385 | } 386 | }, 387 | "node_modules/graceful-fs": { 388 | "version": "4.2.3", 389 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 390 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", 391 | "dev": true 392 | }, 393 | "node_modules/has": { 394 | "version": "1.0.3", 395 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 396 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 397 | "dev": true, 398 | "dependencies": { 399 | "function-bind": "^1.1.1" 400 | }, 401 | "engines": { 402 | "node": ">= 0.4.0" 403 | } 404 | }, 405 | "node_modules/has-flag": { 406 | "version": "3.0.0", 407 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 408 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 409 | "dev": true, 410 | "engines": { 411 | "node": ">=4" 412 | } 413 | }, 414 | "node_modules/inflight": { 415 | "version": "1.0.6", 416 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 417 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 418 | "dev": true, 419 | "dependencies": { 420 | "once": "^1.3.0", 421 | "wrappy": "1" 422 | } 423 | }, 424 | "node_modules/inherits": { 425 | "version": "2.0.3", 426 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 427 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 428 | "dev": true 429 | }, 430 | "node_modules/is-core-module": { 431 | "version": "2.4.0", 432 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", 433 | "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", 434 | "dev": true, 435 | "dependencies": { 436 | "has": "^1.0.3" 437 | }, 438 | "funding": { 439 | "url": "https://github.com/sponsors/ljharb" 440 | } 441 | }, 442 | "node_modules/is-module": { 443 | "version": "1.0.0", 444 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 445 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", 446 | "dev": true 447 | }, 448 | "node_modules/is-reference": { 449 | "version": "1.2.1", 450 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", 451 | "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", 452 | "dev": true, 453 | "dependencies": { 454 | "@types/estree": "*" 455 | } 456 | }, 457 | "node_modules/jest-worker": { 458 | "version": "26.6.2", 459 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", 460 | "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", 461 | "dev": true, 462 | "dependencies": { 463 | "@types/node": "*", 464 | "merge-stream": "^2.0.0", 465 | "supports-color": "^7.0.0" 466 | }, 467 | "engines": { 468 | "node": ">= 10.13.0" 469 | } 470 | }, 471 | "node_modules/jest-worker/node_modules/has-flag": { 472 | "version": "4.0.0", 473 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 474 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 475 | "dev": true, 476 | "engines": { 477 | "node": ">=8" 478 | } 479 | }, 480 | "node_modules/jest-worker/node_modules/supports-color": { 481 | "version": "7.2.0", 482 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 483 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 484 | "dev": true, 485 | "dependencies": { 486 | "has-flag": "^4.0.0" 487 | }, 488 | "engines": { 489 | "node": ">=8" 490 | } 491 | }, 492 | "node_modules/js-tokens": { 493 | "version": "4.0.0", 494 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 495 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 496 | "dev": true 497 | }, 498 | "node_modules/jsonfile": { 499 | "version": "6.1.0", 500 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", 501 | "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", 502 | "dev": true, 503 | "dependencies": { 504 | "universalify": "^2.0.0" 505 | }, 506 | "optionalDependencies": { 507 | "graceful-fs": "^4.1.6" 508 | } 509 | }, 510 | "node_modules/lodash": { 511 | "version": "4.17.21", 512 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 513 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 514 | "dev": true 515 | }, 516 | "node_modules/magic-string": { 517 | "version": "0.25.7", 518 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", 519 | "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", 520 | "dev": true, 521 | "dependencies": { 522 | "sourcemap-codec": "^1.4.4" 523 | } 524 | }, 525 | "node_modules/merge-stream": { 526 | "version": "2.0.0", 527 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 528 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 529 | "dev": true 530 | }, 531 | "node_modules/minimatch": { 532 | "version": "3.0.4", 533 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 534 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 535 | "dev": true, 536 | "dependencies": { 537 | "brace-expansion": "^1.1.7" 538 | }, 539 | "engines": { 540 | "node": "*" 541 | } 542 | }, 543 | "node_modules/moment": { 544 | "version": "2.29.1", 545 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", 546 | "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", 547 | "dev": true, 548 | "engines": { 549 | "node": "*" 550 | } 551 | }, 552 | "node_modules/once": { 553 | "version": "1.4.0", 554 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 555 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 556 | "dev": true, 557 | "dependencies": { 558 | "wrappy": "1" 559 | } 560 | }, 561 | "node_modules/package-name-regex": { 562 | "version": "1.0.9", 563 | "resolved": "https://registry.npmjs.org/package-name-regex/-/package-name-regex-1.0.9.tgz", 564 | "integrity": "sha512-+U2oQCfEz2IlGqws8gmfKzdMDbSd6+RZp6UIFdKo+GAw3+o+kfnsgXkWtJ1JMoKhpP2kEvuYyTy1lXOEQEe0ZA==", 565 | "dev": true 566 | }, 567 | "node_modules/path-is-absolute": { 568 | "version": "1.0.1", 569 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 570 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 571 | "dev": true, 572 | "engines": { 573 | "node": ">=0.10.0" 574 | } 575 | }, 576 | "node_modules/path-parse": { 577 | "version": "1.0.6", 578 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 579 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 580 | "dev": true 581 | }, 582 | "node_modules/picomatch": { 583 | "version": "2.3.0", 584 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", 585 | "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", 586 | "dev": true, 587 | "engines": { 588 | "node": ">=8.6" 589 | }, 590 | "funding": { 591 | "url": "https://github.com/sponsors/jonschlinkert" 592 | } 593 | }, 594 | "node_modules/randombytes": { 595 | "version": "2.1.0", 596 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 597 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 598 | "dev": true, 599 | "dependencies": { 600 | "safe-buffer": "^5.1.0" 601 | } 602 | }, 603 | "node_modules/resolve": { 604 | "version": "1.20.0", 605 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", 606 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", 607 | "dev": true, 608 | "dependencies": { 609 | "is-core-module": "^2.2.0", 610 | "path-parse": "^1.0.6" 611 | }, 612 | "funding": { 613 | "url": "https://github.com/sponsors/ljharb" 614 | } 615 | }, 616 | "node_modules/rimraf": { 617 | "version": "3.0.2", 618 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 619 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 620 | "dev": true, 621 | "dependencies": { 622 | "glob": "^7.1.3" 623 | }, 624 | "bin": { 625 | "rimraf": "bin.js" 626 | }, 627 | "funding": { 628 | "url": "https://github.com/sponsors/isaacs" 629 | } 630 | }, 631 | "node_modules/rollup": { 632 | "version": "2.47.0", 633 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.47.0.tgz", 634 | "integrity": "sha512-rqBjgq9hQfW0vRmz+0S062ORRNJXvwRpzxhFXORvar/maZqY6za3rgQ/p1Glg+j1hnc1GtYyQCPiAei95uTElg==", 635 | "dev": true, 636 | "bin": { 637 | "rollup": "dist/bin/rollup" 638 | }, 639 | "engines": { 640 | "node": ">=10.0.0" 641 | }, 642 | "optionalDependencies": { 643 | "fsevents": "~2.3.1" 644 | } 645 | }, 646 | "node_modules/rollup-plugin-license": { 647 | "version": "2.3.0", 648 | "resolved": "https://registry.npmjs.org/rollup-plugin-license/-/rollup-plugin-license-2.3.0.tgz", 649 | "integrity": "sha512-oi8pL59fVTwXCkLUsZ8dVGVJjO7Hcc5UT0chJvKd0MktPgeYHSadkaicAYUemdYHHpjb0D3DyvedZAEPt+2r8w==", 650 | "dev": true, 651 | "dependencies": { 652 | "commenting": "1.1.0", 653 | "glob": "7.1.6", 654 | "lodash": "4.17.21", 655 | "magic-string": "0.25.7", 656 | "mkdirp": "1.0.4", 657 | "moment": "2.29.1", 658 | "package-name-regex": "1.0.9", 659 | "spdx-expression-validate": "2.0.0", 660 | "spdx-satisfies": "5.0.0" 661 | }, 662 | "engines": { 663 | "node": ">=10.0.0" 664 | }, 665 | "peerDependencies": { 666 | "rollup": "^1.0.0 || ^2.0.0" 667 | } 668 | }, 669 | "node_modules/rollup-plugin-license/node_modules/mkdirp": { 670 | "version": "1.0.4", 671 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 672 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 673 | "dev": true, 674 | "bin": { 675 | "mkdirp": "bin/cmd.js" 676 | }, 677 | "engines": { 678 | "node": ">=10" 679 | } 680 | }, 681 | "node_modules/rollup-plugin-terser": { 682 | "version": "7.0.2", 683 | "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", 684 | "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", 685 | "dev": true, 686 | "dependencies": { 687 | "@babel/code-frame": "^7.10.4", 688 | "jest-worker": "^26.2.1", 689 | "serialize-javascript": "^4.0.0", 690 | "terser": "^5.0.0" 691 | }, 692 | "peerDependencies": { 693 | "rollup": "^2.0.0" 694 | } 695 | }, 696 | "node_modules/rollup-plugin-terser/node_modules/commander": { 697 | "version": "2.20.3", 698 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 699 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 700 | "dev": true 701 | }, 702 | "node_modules/rollup-plugin-terser/node_modules/serialize-javascript": { 703 | "version": "4.0.0", 704 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", 705 | "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", 706 | "dev": true, 707 | "dependencies": { 708 | "randombytes": "^2.1.0" 709 | } 710 | }, 711 | "node_modules/rollup-plugin-terser/node_modules/source-map": { 712 | "version": "0.7.3", 713 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", 714 | "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", 715 | "dev": true, 716 | "engines": { 717 | "node": ">= 8" 718 | } 719 | }, 720 | "node_modules/rollup-plugin-terser/node_modules/terser": { 721 | "version": "5.7.1", 722 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", 723 | "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", 724 | "dev": true, 725 | "dependencies": { 726 | "commander": "^2.20.0", 727 | "source-map": "~0.7.2", 728 | "source-map-support": "~0.5.19" 729 | }, 730 | "bin": { 731 | "terser": "bin/terser" 732 | }, 733 | "engines": { 734 | "node": ">=10" 735 | } 736 | }, 737 | "node_modules/rollup/node_modules/fsevents": { 738 | "version": "2.3.2", 739 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 740 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 741 | "dev": true, 742 | "hasInstallScript": true, 743 | "optional": true, 744 | "os": [ 745 | "darwin" 746 | ], 747 | "engines": { 748 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 749 | } 750 | }, 751 | "node_modules/safe-buffer": { 752 | "version": "5.1.2", 753 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 754 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 755 | "dev": true 756 | }, 757 | "node_modules/source-map": { 758 | "version": "0.6.1", 759 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 760 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 761 | "dev": true, 762 | "engines": { 763 | "node": ">=0.10.0" 764 | } 765 | }, 766 | "node_modules/source-map-support": { 767 | "version": "0.5.19", 768 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 769 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 770 | "dev": true, 771 | "dependencies": { 772 | "buffer-from": "^1.0.0", 773 | "source-map": "^0.6.0" 774 | } 775 | }, 776 | "node_modules/sourcemap-codec": { 777 | "version": "1.4.8", 778 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", 779 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", 780 | "dev": true 781 | }, 782 | "node_modules/spdx-compare": { 783 | "version": "1.0.0", 784 | "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", 785 | "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", 786 | "dev": true, 787 | "dependencies": { 788 | "array-find-index": "^1.0.2", 789 | "spdx-expression-parse": "^3.0.0", 790 | "spdx-ranges": "^2.0.0" 791 | } 792 | }, 793 | "node_modules/spdx-exceptions": { 794 | "version": "2.2.0", 795 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 796 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", 797 | "dev": true 798 | }, 799 | "node_modules/spdx-expression-parse": { 800 | "version": "3.0.0", 801 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 802 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 803 | "dev": true, 804 | "dependencies": { 805 | "spdx-exceptions": "^2.1.0", 806 | "spdx-license-ids": "^3.0.0" 807 | } 808 | }, 809 | "node_modules/spdx-expression-validate": { 810 | "version": "2.0.0", 811 | "resolved": "https://registry.npmjs.org/spdx-expression-validate/-/spdx-expression-validate-2.0.0.tgz", 812 | "integrity": "sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg==", 813 | "dev": true, 814 | "dependencies": { 815 | "spdx-expression-parse": "^3.0.0" 816 | } 817 | }, 818 | "node_modules/spdx-license-ids": { 819 | "version": "3.0.5", 820 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", 821 | "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", 822 | "dev": true 823 | }, 824 | "node_modules/spdx-ranges": { 825 | "version": "2.1.1", 826 | "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", 827 | "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", 828 | "dev": true 829 | }, 830 | "node_modules/spdx-satisfies": { 831 | "version": "5.0.0", 832 | "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-5.0.0.tgz", 833 | "integrity": "sha512-/hGhwh20BeGmkA+P/lm06RvXD94JduwNxtx/oX3B5ClPt1/u/m5MCaDNo1tV3Y9laLkQr/NRde63b9lLMhlNfw==", 834 | "dev": true, 835 | "dependencies": { 836 | "spdx-compare": "^1.0.0", 837 | "spdx-expression-parse": "^3.0.0", 838 | "spdx-ranges": "^2.0.0" 839 | } 840 | }, 841 | "node_modules/supports-color": { 842 | "version": "5.5.0", 843 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 844 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 845 | "dev": true, 846 | "dependencies": { 847 | "has-flag": "^3.0.0" 848 | }, 849 | "engines": { 850 | "node": ">=4" 851 | } 852 | }, 853 | "node_modules/tslib": { 854 | "version": "2.3.0", 855 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", 856 | "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", 857 | "dev": true 858 | }, 859 | "node_modules/typescript": { 860 | "version": "4.2.4", 861 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", 862 | "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", 863 | "dev": true, 864 | "bin": { 865 | "tsc": "bin/tsc", 866 | "tsserver": "bin/tsserver" 867 | }, 868 | "engines": { 869 | "node": ">=4.2.0" 870 | } 871 | }, 872 | "node_modules/universalify": { 873 | "version": "2.0.0", 874 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", 875 | "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", 876 | "dev": true, 877 | "engines": { 878 | "node": ">= 10.0.0" 879 | } 880 | }, 881 | "node_modules/wrappy": { 882 | "version": "1.0.2", 883 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 884 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 885 | "dev": true 886 | } 887 | }, 888 | "dependencies": { 889 | "@babel/code-frame": { 890 | "version": "7.14.5", 891 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", 892 | "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", 893 | "dev": true, 894 | "requires": { 895 | "@babel/highlight": "^7.14.5" 896 | } 897 | }, 898 | "@babel/helper-validator-identifier": { 899 | "version": "7.14.5", 900 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz", 901 | "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==", 902 | "dev": true 903 | }, 904 | "@babel/highlight": { 905 | "version": "7.14.5", 906 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", 907 | "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", 908 | "dev": true, 909 | "requires": { 910 | "@babel/helper-validator-identifier": "^7.14.5", 911 | "chalk": "^2.0.0", 912 | "js-tokens": "^4.0.0" 913 | } 914 | }, 915 | "@rollup/plugin-commonjs": { 916 | "version": "19.0.0", 917 | "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-19.0.0.tgz", 918 | "integrity": "sha512-adTpD6ATGbehdaQoZQ6ipDFhdjqsTgpOAhFiPwl+dzre4pPshsecptDPyEFb61JMJ1+mGljktaC4jI8ARMSNyw==", 919 | "dev": true, 920 | "requires": { 921 | "@rollup/pluginutils": "^3.1.0", 922 | "commondir": "^1.0.1", 923 | "estree-walker": "^2.0.1", 924 | "glob": "^7.1.6", 925 | "is-reference": "^1.2.1", 926 | "magic-string": "^0.25.7", 927 | "resolve": "^1.17.0" 928 | } 929 | }, 930 | "@rollup/plugin-node-resolve": { 931 | "version": "13.0.0", 932 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.0.tgz", 933 | "integrity": "sha512-41X411HJ3oikIDivT5OKe9EZ6ud6DXudtfNrGbC4nniaxx2esiWjkLOzgnZsWq1IM8YIeL2rzRGLZLBjlhnZtQ==", 934 | "dev": true, 935 | "requires": { 936 | "@rollup/pluginutils": "^3.1.0", 937 | "@types/resolve": "1.17.1", 938 | "builtin-modules": "^3.1.0", 939 | "deepmerge": "^4.2.2", 940 | "is-module": "^1.0.0", 941 | "resolve": "^1.19.0" 942 | }, 943 | "dependencies": { 944 | "builtin-modules": { 945 | "version": "3.2.0", 946 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", 947 | "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", 948 | "dev": true 949 | } 950 | } 951 | }, 952 | "@rollup/plugin-typescript": { 953 | "version": "8.2.1", 954 | "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.1.tgz", 955 | "integrity": "sha512-Qd2E1pleDR4bwyFxqbjt4eJf+wB0UKVMLc7/BAFDGVdAXQMCsD4DUv5/7/ww47BZCYxWtJqe1Lo0KVNswBJlRw==", 956 | "dev": true, 957 | "requires": { 958 | "@rollup/pluginutils": "^3.1.0", 959 | "resolve": "^1.17.0" 960 | } 961 | }, 962 | "@rollup/pluginutils": { 963 | "version": "3.1.0", 964 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", 965 | "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", 966 | "dev": true, 967 | "requires": { 968 | "@types/estree": "0.0.39", 969 | "estree-walker": "^1.0.1", 970 | "picomatch": "^2.2.2" 971 | }, 972 | "dependencies": { 973 | "estree-walker": { 974 | "version": "1.0.1", 975 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", 976 | "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", 977 | "dev": true 978 | } 979 | } 980 | }, 981 | "@types/backbone": { 982 | "version": "1.4.1", 983 | "resolved": "https://registry.npmjs.org/@types/backbone/-/backbone-1.4.1.tgz", 984 | "integrity": "sha512-KYfGuQy4d2vvYXbn0uHFZ6brFLndatTMomxBlljpbWf4kFpA3BG/6LA3ec+J9iredrX6eAVI7sm9SVAvwiIM6g==", 985 | "dev": true, 986 | "requires": { 987 | "@types/jquery": "*", 988 | "@types/underscore": "*" 989 | } 990 | }, 991 | "@types/estree": { 992 | "version": "0.0.39", 993 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", 994 | "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", 995 | "dev": true 996 | }, 997 | "@types/jquery": { 998 | "version": "3.5.6", 999 | "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.6.tgz", 1000 | "integrity": "sha512-SmgCQRzGPId4MZQKDj9Hqc6kSXFNWZFHpELkyK8AQhf8Zr6HKfCzFv9ZC1Fv3FyQttJZOlap3qYb12h61iZAIg==", 1001 | "dev": true, 1002 | "requires": { 1003 | "@types/sizzle": "*" 1004 | } 1005 | }, 1006 | "@types/node": { 1007 | "version": "13.9.1", 1008 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.1.tgz", 1009 | "integrity": "sha512-E6M6N0blf/jiZx8Q3nb0vNaswQeEyn0XlupO+xN6DtJ6r6IT4nXrTry7zhIfYvFCl3/8Cu6WIysmUBKiqV0bqQ==", 1010 | "dev": true 1011 | }, 1012 | "@types/resolve": { 1013 | "version": "1.17.1", 1014 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", 1015 | "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", 1016 | "dev": true, 1017 | "requires": { 1018 | "@types/node": "*" 1019 | } 1020 | }, 1021 | "@types/sizzle": { 1022 | "version": "2.3.3", 1023 | "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", 1024 | "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", 1025 | "dev": true 1026 | }, 1027 | "@types/underscore": { 1028 | "version": "1.11.3", 1029 | "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.3.tgz", 1030 | "integrity": "sha512-Fl1TX1dapfXyDqFg2ic9M+vlXRktcPJrc4PR7sRc7sdVrjavg/JHlbUXBt8qWWqhJrmSqg3RNAkAPRiOYw6Ahw==", 1031 | "dev": true 1032 | }, 1033 | "ace-builds": { 1034 | "version": "1.4.12", 1035 | "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.12.tgz", 1036 | "integrity": "sha512-G+chJctFPiiLGvs3+/Mly3apXTcfgE45dT5yp12BcWZ1kUs+gm0qd3/fv4gsz6fVag4mM0moHVpjHDIgph6Psg==" 1037 | }, 1038 | "ansi-styles": { 1039 | "version": "3.2.1", 1040 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 1041 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 1042 | "dev": true, 1043 | "requires": { 1044 | "color-convert": "^1.9.0" 1045 | } 1046 | }, 1047 | "array-find-index": { 1048 | "version": "1.0.2", 1049 | "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", 1050 | "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", 1051 | "dev": true 1052 | }, 1053 | "balanced-match": { 1054 | "version": "1.0.0", 1055 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 1056 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 1057 | "dev": true 1058 | }, 1059 | "brace-expansion": { 1060 | "version": "1.1.11", 1061 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1062 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1063 | "dev": true, 1064 | "requires": { 1065 | "balanced-match": "^1.0.0", 1066 | "concat-map": "0.0.1" 1067 | } 1068 | }, 1069 | "buffer-from": { 1070 | "version": "1.1.1", 1071 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 1072 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 1073 | "dev": true 1074 | }, 1075 | "chalk": { 1076 | "version": "2.4.2", 1077 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 1078 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 1079 | "dev": true, 1080 | "requires": { 1081 | "ansi-styles": "^3.2.1", 1082 | "escape-string-regexp": "^1.0.5", 1083 | "supports-color": "^5.3.0" 1084 | } 1085 | }, 1086 | "clean-css": { 1087 | "version": "5.1.3", 1088 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.1.3.tgz", 1089 | "integrity": "sha512-qGXzUCDpLwAlPx0kYeU4QXjzQIcIYZbJjD4FNm7NnSjoP0hYMVZhHOpUYJ6AwfkMX2cceLRq54MeCgHy/va1cA==", 1090 | "dev": true, 1091 | "requires": { 1092 | "source-map": "~0.6.0" 1093 | } 1094 | }, 1095 | "color-convert": { 1096 | "version": "1.9.3", 1097 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 1098 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 1099 | "dev": true, 1100 | "requires": { 1101 | "color-name": "1.1.3" 1102 | } 1103 | }, 1104 | "color-name": { 1105 | "version": "1.1.3", 1106 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 1107 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 1108 | "dev": true 1109 | }, 1110 | "commenting": { 1111 | "version": "1.1.0", 1112 | "resolved": "https://registry.npmjs.org/commenting/-/commenting-1.1.0.tgz", 1113 | "integrity": "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA==", 1114 | "dev": true 1115 | }, 1116 | "commondir": { 1117 | "version": "1.0.1", 1118 | "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 1119 | "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", 1120 | "dev": true 1121 | }, 1122 | "concat-map": { 1123 | "version": "0.0.1", 1124 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1125 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 1126 | "dev": true 1127 | }, 1128 | "deepmerge": { 1129 | "version": "4.2.2", 1130 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", 1131 | "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", 1132 | "dev": true 1133 | }, 1134 | "escape-string-regexp": { 1135 | "version": "1.0.5", 1136 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 1137 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 1138 | "dev": true 1139 | }, 1140 | "estree-walker": { 1141 | "version": "2.0.2", 1142 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", 1143 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", 1144 | "dev": true 1145 | }, 1146 | "fs-extra": { 1147 | "version": "10.0.0", 1148 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", 1149 | "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", 1150 | "dev": true, 1151 | "requires": { 1152 | "graceful-fs": "^4.2.0", 1153 | "jsonfile": "^6.0.1", 1154 | "universalify": "^2.0.0" 1155 | } 1156 | }, 1157 | "fs.realpath": { 1158 | "version": "1.0.0", 1159 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1160 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 1161 | "dev": true 1162 | }, 1163 | "function-bind": { 1164 | "version": "1.1.1", 1165 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1166 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 1167 | "dev": true 1168 | }, 1169 | "glob": { 1170 | "version": "7.1.6", 1171 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 1172 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 1173 | "dev": true, 1174 | "requires": { 1175 | "fs.realpath": "^1.0.0", 1176 | "inflight": "^1.0.4", 1177 | "inherits": "2", 1178 | "minimatch": "^3.0.4", 1179 | "once": "^1.3.0", 1180 | "path-is-absolute": "^1.0.0" 1181 | } 1182 | }, 1183 | "graceful-fs": { 1184 | "version": "4.2.3", 1185 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", 1186 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", 1187 | "dev": true 1188 | }, 1189 | "has": { 1190 | "version": "1.0.3", 1191 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1192 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1193 | "dev": true, 1194 | "requires": { 1195 | "function-bind": "^1.1.1" 1196 | } 1197 | }, 1198 | "has-flag": { 1199 | "version": "3.0.0", 1200 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1201 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 1202 | "dev": true 1203 | }, 1204 | "inflight": { 1205 | "version": "1.0.6", 1206 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1207 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 1208 | "dev": true, 1209 | "requires": { 1210 | "once": "^1.3.0", 1211 | "wrappy": "1" 1212 | } 1213 | }, 1214 | "inherits": { 1215 | "version": "2.0.3", 1216 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 1217 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 1218 | "dev": true 1219 | }, 1220 | "is-core-module": { 1221 | "version": "2.4.0", 1222 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", 1223 | "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", 1224 | "dev": true, 1225 | "requires": { 1226 | "has": "^1.0.3" 1227 | } 1228 | }, 1229 | "is-module": { 1230 | "version": "1.0.0", 1231 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 1232 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", 1233 | "dev": true 1234 | }, 1235 | "is-reference": { 1236 | "version": "1.2.1", 1237 | "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", 1238 | "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", 1239 | "dev": true, 1240 | "requires": { 1241 | "@types/estree": "*" 1242 | } 1243 | }, 1244 | "jest-worker": { 1245 | "version": "26.6.2", 1246 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", 1247 | "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", 1248 | "dev": true, 1249 | "requires": { 1250 | "@types/node": "*", 1251 | "merge-stream": "^2.0.0", 1252 | "supports-color": "^7.0.0" 1253 | }, 1254 | "dependencies": { 1255 | "has-flag": { 1256 | "version": "4.0.0", 1257 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1258 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1259 | "dev": true 1260 | }, 1261 | "supports-color": { 1262 | "version": "7.2.0", 1263 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1264 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1265 | "dev": true, 1266 | "requires": { 1267 | "has-flag": "^4.0.0" 1268 | } 1269 | } 1270 | } 1271 | }, 1272 | "js-tokens": { 1273 | "version": "4.0.0", 1274 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1275 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 1276 | "dev": true 1277 | }, 1278 | "jsonfile": { 1279 | "version": "6.1.0", 1280 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", 1281 | "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", 1282 | "dev": true, 1283 | "requires": { 1284 | "graceful-fs": "^4.1.6", 1285 | "universalify": "^2.0.0" 1286 | } 1287 | }, 1288 | "lodash": { 1289 | "version": "4.17.21", 1290 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 1291 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", 1292 | "dev": true 1293 | }, 1294 | "magic-string": { 1295 | "version": "0.25.7", 1296 | "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", 1297 | "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", 1298 | "dev": true, 1299 | "requires": { 1300 | "sourcemap-codec": "^1.4.4" 1301 | } 1302 | }, 1303 | "merge-stream": { 1304 | "version": "2.0.0", 1305 | "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 1306 | "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 1307 | "dev": true 1308 | }, 1309 | "minimatch": { 1310 | "version": "3.0.4", 1311 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1312 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1313 | "dev": true, 1314 | "requires": { 1315 | "brace-expansion": "^1.1.7" 1316 | } 1317 | }, 1318 | "moment": { 1319 | "version": "2.29.1", 1320 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", 1321 | "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==", 1322 | "dev": true 1323 | }, 1324 | "once": { 1325 | "version": "1.4.0", 1326 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1327 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1328 | "dev": true, 1329 | "requires": { 1330 | "wrappy": "1" 1331 | } 1332 | }, 1333 | "package-name-regex": { 1334 | "version": "1.0.9", 1335 | "resolved": "https://registry.npmjs.org/package-name-regex/-/package-name-regex-1.0.9.tgz", 1336 | "integrity": "sha512-+U2oQCfEz2IlGqws8gmfKzdMDbSd6+RZp6UIFdKo+GAw3+o+kfnsgXkWtJ1JMoKhpP2kEvuYyTy1lXOEQEe0ZA==", 1337 | "dev": true 1338 | }, 1339 | "path-is-absolute": { 1340 | "version": "1.0.1", 1341 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1342 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1343 | "dev": true 1344 | }, 1345 | "path-parse": { 1346 | "version": "1.0.6", 1347 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 1348 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 1349 | "dev": true 1350 | }, 1351 | "picomatch": { 1352 | "version": "2.3.0", 1353 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", 1354 | "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", 1355 | "dev": true 1356 | }, 1357 | "randombytes": { 1358 | "version": "2.1.0", 1359 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 1360 | "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 1361 | "dev": true, 1362 | "requires": { 1363 | "safe-buffer": "^5.1.0" 1364 | } 1365 | }, 1366 | "resolve": { 1367 | "version": "1.20.0", 1368 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", 1369 | "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", 1370 | "dev": true, 1371 | "requires": { 1372 | "is-core-module": "^2.2.0", 1373 | "path-parse": "^1.0.6" 1374 | } 1375 | }, 1376 | "rimraf": { 1377 | "version": "3.0.2", 1378 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1379 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1380 | "dev": true, 1381 | "requires": { 1382 | "glob": "^7.1.3" 1383 | } 1384 | }, 1385 | "rollup": { 1386 | "version": "2.47.0", 1387 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.47.0.tgz", 1388 | "integrity": "sha512-rqBjgq9hQfW0vRmz+0S062ORRNJXvwRpzxhFXORvar/maZqY6za3rgQ/p1Glg+j1hnc1GtYyQCPiAei95uTElg==", 1389 | "dev": true, 1390 | "requires": { 1391 | "fsevents": "~2.3.1" 1392 | }, 1393 | "dependencies": { 1394 | "fsevents": { 1395 | "version": "2.3.2", 1396 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1397 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1398 | "dev": true, 1399 | "optional": true 1400 | } 1401 | } 1402 | }, 1403 | "rollup-plugin-license": { 1404 | "version": "2.3.0", 1405 | "resolved": "https://registry.npmjs.org/rollup-plugin-license/-/rollup-plugin-license-2.3.0.tgz", 1406 | "integrity": "sha512-oi8pL59fVTwXCkLUsZ8dVGVJjO7Hcc5UT0chJvKd0MktPgeYHSadkaicAYUemdYHHpjb0D3DyvedZAEPt+2r8w==", 1407 | "dev": true, 1408 | "requires": { 1409 | "commenting": "1.1.0", 1410 | "glob": "7.1.6", 1411 | "lodash": "4.17.21", 1412 | "magic-string": "0.25.7", 1413 | "mkdirp": "1.0.4", 1414 | "moment": "2.29.1", 1415 | "package-name-regex": "1.0.9", 1416 | "spdx-expression-validate": "2.0.0", 1417 | "spdx-satisfies": "5.0.0" 1418 | }, 1419 | "dependencies": { 1420 | "mkdirp": { 1421 | "version": "1.0.4", 1422 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 1423 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 1424 | "dev": true 1425 | } 1426 | } 1427 | }, 1428 | "rollup-plugin-terser": { 1429 | "version": "7.0.2", 1430 | "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", 1431 | "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", 1432 | "dev": true, 1433 | "requires": { 1434 | "@babel/code-frame": "^7.10.4", 1435 | "jest-worker": "^26.2.1", 1436 | "serialize-javascript": "^4.0.0", 1437 | "terser": "^5.0.0" 1438 | }, 1439 | "dependencies": { 1440 | "commander": { 1441 | "version": "2.20.3", 1442 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 1443 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 1444 | "dev": true 1445 | }, 1446 | "serialize-javascript": { 1447 | "version": "4.0.0", 1448 | "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", 1449 | "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", 1450 | "dev": true, 1451 | "requires": { 1452 | "randombytes": "^2.1.0" 1453 | } 1454 | }, 1455 | "source-map": { 1456 | "version": "0.7.3", 1457 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", 1458 | "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", 1459 | "dev": true 1460 | }, 1461 | "terser": { 1462 | "version": "5.7.1", 1463 | "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", 1464 | "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", 1465 | "dev": true, 1466 | "requires": { 1467 | "commander": "^2.20.0", 1468 | "source-map": "~0.7.2", 1469 | "source-map-support": "~0.5.19" 1470 | } 1471 | } 1472 | } 1473 | }, 1474 | "safe-buffer": { 1475 | "version": "5.1.2", 1476 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1477 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1478 | "dev": true 1479 | }, 1480 | "source-map": { 1481 | "version": "0.6.1", 1482 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1483 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1484 | "dev": true 1485 | }, 1486 | "source-map-support": { 1487 | "version": "0.5.19", 1488 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 1489 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 1490 | "dev": true, 1491 | "requires": { 1492 | "buffer-from": "^1.0.0", 1493 | "source-map": "^0.6.0" 1494 | } 1495 | }, 1496 | "sourcemap-codec": { 1497 | "version": "1.4.8", 1498 | "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", 1499 | "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", 1500 | "dev": true 1501 | }, 1502 | "spdx-compare": { 1503 | "version": "1.0.0", 1504 | "resolved": "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz", 1505 | "integrity": "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A==", 1506 | "dev": true, 1507 | "requires": { 1508 | "array-find-index": "^1.0.2", 1509 | "spdx-expression-parse": "^3.0.0", 1510 | "spdx-ranges": "^2.0.0" 1511 | } 1512 | }, 1513 | "spdx-exceptions": { 1514 | "version": "2.2.0", 1515 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 1516 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", 1517 | "dev": true 1518 | }, 1519 | "spdx-expression-parse": { 1520 | "version": "3.0.0", 1521 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 1522 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 1523 | "dev": true, 1524 | "requires": { 1525 | "spdx-exceptions": "^2.1.0", 1526 | "spdx-license-ids": "^3.0.0" 1527 | } 1528 | }, 1529 | "spdx-expression-validate": { 1530 | "version": "2.0.0", 1531 | "resolved": "https://registry.npmjs.org/spdx-expression-validate/-/spdx-expression-validate-2.0.0.tgz", 1532 | "integrity": "sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg==", 1533 | "dev": true, 1534 | "requires": { 1535 | "spdx-expression-parse": "^3.0.0" 1536 | } 1537 | }, 1538 | "spdx-license-ids": { 1539 | "version": "3.0.5", 1540 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", 1541 | "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", 1542 | "dev": true 1543 | }, 1544 | "spdx-ranges": { 1545 | "version": "2.1.1", 1546 | "resolved": "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz", 1547 | "integrity": "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA==", 1548 | "dev": true 1549 | }, 1550 | "spdx-satisfies": { 1551 | "version": "5.0.0", 1552 | "resolved": "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-5.0.0.tgz", 1553 | "integrity": "sha512-/hGhwh20BeGmkA+P/lm06RvXD94JduwNxtx/oX3B5ClPt1/u/m5MCaDNo1tV3Y9laLkQr/NRde63b9lLMhlNfw==", 1554 | "dev": true, 1555 | "requires": { 1556 | "spdx-compare": "^1.0.0", 1557 | "spdx-expression-parse": "^3.0.0", 1558 | "spdx-ranges": "^2.0.0" 1559 | } 1560 | }, 1561 | "supports-color": { 1562 | "version": "5.5.0", 1563 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1564 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1565 | "dev": true, 1566 | "requires": { 1567 | "has-flag": "^3.0.0" 1568 | } 1569 | }, 1570 | "tslib": { 1571 | "version": "2.3.0", 1572 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", 1573 | "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", 1574 | "dev": true 1575 | }, 1576 | "typescript": { 1577 | "version": "4.2.4", 1578 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz", 1579 | "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==", 1580 | "dev": true 1581 | }, 1582 | "universalify": { 1583 | "version": "2.0.0", 1584 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", 1585 | "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", 1586 | "dev": true 1587 | }, 1588 | "wrappy": { 1589 | "version": "1.0.2", 1590 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1591 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1592 | "dev": true 1593 | } 1594 | } 1595 | } 1596 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@convergencelabs/ace-collab-ext", 3 | "version": "0.6.0", 4 | "title": "Ace Editor Collaborative Extensions", 5 | "description": "Collaborative Extensions for the Ace Editor", 6 | "keywords": [ 7 | "collaboration", 8 | "ace", 9 | "editor" 10 | ], 11 | "homepage": "http://convergencelabs.com", 12 | "author": { 13 | "name": "Convergence Labs", 14 | "email": "info@convergencelabs.com", 15 | "url": "http://convergencelabs.com" 16 | }, 17 | "contributors": [], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/convergencelabs/ace-collab-ext.git" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/convergencelabs/ace-collab-ext/issues" 24 | }, 25 | "license": "MIT", 26 | "scripts": { 27 | "build:esm": "tsc --module ES2020 --target ES2020 --outDir dist/module", 28 | "build:commonjs": "tsc --module commonjs --target es5 --outDir dist/lib", 29 | "build:types": "tsc --declaration true --emitDeclarationOnly true --outDir dist/types && node ./scripts/enhance-types.js", 30 | "build:umd": "rollup -c rollup.config.js", 31 | "build:css": "node scripts/build-css.js", 32 | "dist": "npm run build:esm && npm run build:commonjs && npm run build:umd && npm run build:types && npm run build:css", 33 | "clean": "rimraf dist", 34 | "prepack": "npm run dist" 35 | }, 36 | "publishConfig": { 37 | "registry": "https://registry.npmjs.org/", 38 | "access": "public" 39 | }, 40 | "main": "dist/lib/index.js", 41 | "module": "dist/module/index.js", 42 | "types": "dist/types/index.d.ts", 43 | "browser": "dist/umd/ace-collab-ext.js", 44 | "files": [ 45 | "dist", 46 | "example" 47 | ], 48 | "dependencies": { 49 | "ace-builds": "^1.4.12" 50 | }, 51 | "devDependencies": { 52 | "@rollup/plugin-commonjs": "19.0.0", 53 | "@rollup/plugin-node-resolve": "13.0.0", 54 | "@rollup/plugin-typescript": "8.2.1", 55 | "@types/backbone": "1.4.1", 56 | "clean-css": "^5.1.3", 57 | "fs-extra": "^10.0.0", 58 | "rimraf": "^3.0.2", 59 | "rollup": "2.47.0", 60 | "rollup-plugin-license": "2.3.0", 61 | "rollup-plugin-terser": "7.0.2", 62 | "tslib": "^2.3.0", 63 | "typescript": "4.2.4" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | // rollup.config.js 2 | import typescript from '@rollup/plugin-typescript'; 3 | import resolve from '@rollup/plugin-node-resolve'; 4 | import commonjs from '@rollup/plugin-commonjs'; 5 | import license from "rollup-plugin-license"; 6 | import {terser} from "rollup-plugin-terser"; 7 | import path from "path"; 8 | 9 | import pkg from './package.json'; 10 | 11 | // 12 | // Commons Settings 13 | // 14 | const input = 'src/ts/index.ts'; 15 | 16 | const plugins = [ 17 | resolve(), 18 | commonjs(), 19 | typescript(), 20 | license({ 21 | banner: { 22 | commentStyle: 'ignored', // The default 23 | content: { 24 | file: path.join(__dirname, 'copyright-header.txt'), 25 | }, 26 | } 27 | }) 28 | ]; 29 | 30 | const moduleName = "AceCollabExt"; 31 | const format = "umd"; 32 | 33 | const external = [ 34 | "ace-builds" 35 | ] 36 | 37 | const globals = { 38 | "ace-builds": "ace" 39 | } 40 | 41 | export default [{ 42 | input, 43 | plugins, 44 | external, 45 | output: [ 46 | { 47 | name: moduleName, 48 | file: pkg.browser, 49 | format, 50 | sourcemap: true, 51 | globals 52 | } 53 | ] 54 | }, { 55 | input, 56 | plugins: [...plugins, terser()], 57 | external, 58 | output: [ 59 | { 60 | name: moduleName, 61 | file: `${path.dirname(pkg.browser)}/${path.basename(pkg.browser, ".js")}.min.js`, 62 | format, 63 | sourcemap: true, 64 | globals 65 | } 66 | ] 67 | }]; -------------------------------------------------------------------------------- /scripts/build-css.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const CleanCSS = require('clean-css'); 3 | 4 | fs.copySync('src/css', 'dist/css'); 5 | 6 | const css = fs.readFileSync('dist/css/ace-collab-ext.css'); 7 | const options = { 8 | sourceMap: true, 9 | sourceMapInlineSources: true 10 | } 11 | const minified = new CleanCSS(options).minify(css); 12 | fs.writeFileSync( 'dist/css/ace-collab-ext.min.css', minified.styles ); 13 | fs.writeFileSync( 'dist/css/ace-collab-ext.css.map', minified.sourceMap.toString() ); 14 | -------------------------------------------------------------------------------- /scripts/enhance-types.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | fs.appendFileSync('dist/types/index.d.ts', '\nexport as namespace AceCollabExt;\n'); 4 | 5 | -------------------------------------------------------------------------------- /src/css/ace-collab-ext.css: -------------------------------------------------------------------------------- 1 | .ace-multi-cursor { 2 | position: absolute; 3 | pointer-events: auto; 4 | z-index: 10; 5 | } 6 | 7 | .ace-multi-cursor:before { 8 | content: ""; 9 | width: 6px; 10 | height: 5px; 11 | display: block; 12 | background: inherit; 13 | margin-left: -2px; 14 | margin-top: -5px; 15 | } 16 | 17 | .ace-multi-cursor-tooltip { 18 | position: absolute; 19 | white-space: nowrap; 20 | color: #FFFFFF; 21 | text-shadow: 0 0 1px #000000; 22 | opacity: 1.0; 23 | font-size: 12px; 24 | padding: 2px; 25 | font-family: sans-serif; 26 | 27 | transition: opacity 0.5s ease-out; 28 | -webkit-transition: opacity 0.5s ease-out; 29 | -moz-transition: opacity 0.5s ease-out; 30 | -ms-transition: opacity 0.5s ease-out; 31 | -o-transition: opacity 0.5s ease-out; 32 | } 33 | 34 | .ace-multi-selection { 35 | position: absolute; 36 | pointer-events: auto; 37 | z-index: 10; 38 | opacity: 0.3; 39 | } 40 | 41 | .ace-radar-view { 42 | position: relative; 43 | min-width: 6px; 44 | } 45 | 46 | .ace-radar-view-scroll-indicator { 47 | position: absolute; 48 | left: 0; 49 | right: 0; 50 | border-radius: 4px; 51 | cursor: pointer; 52 | border-style: double; 53 | border-width: 3px; 54 | } 55 | 56 | .ace-radar-view-cursor-indicator { 57 | position: absolute; 58 | left: 0; 59 | right: 0; 60 | height: 4px; 61 | border-radius: 3px; 62 | cursor: pointer; 63 | border: 1px solid black; 64 | } 65 | 66 | .ace-radar-view-wrapper { 67 | position: relative; 68 | float: left; 69 | 70 | height: 100%; 71 | width: 6px; 72 | 73 | margin-right: 4px; 74 | } 75 | -------------------------------------------------------------------------------- /src/ts/AceCursorMarker.ts: -------------------------------------------------------------------------------- 1 | import {Ace} from "ace-builds"; 2 | 3 | /** 4 | * Represents a marker of a remote users cursor. 5 | */ 6 | export class AceCursorMarker implements Ace.MarkerLike { 7 | 8 | public range: Ace.Range; 9 | public type: string; 10 | public renderer?: Ace.MarkerRenderer; 11 | public clazz: string; 12 | public inFront: boolean; 13 | public id: number; 14 | 15 | private readonly _session: Ace.EditSession; 16 | private readonly _label: string; 17 | private readonly _color: string; 18 | private readonly _cursorId: string; 19 | private readonly _id: string; 20 | private readonly _markerElement: HTMLDivElement; 21 | private readonly _cursorElement: HTMLDivElement; 22 | private readonly _tooltipElement: HTMLDivElement; 23 | private _visible: boolean; 24 | private _position: Ace.Point; 25 | private _tooltipTimeout: any; 26 | 27 | /** 28 | * Constructs a new AceCursorMarker 29 | * @param session The Ace Editor Session to bind to. 30 | * @param cursorId the unique id of this cursor. 31 | * @param label The label to display over the cursor. 32 | * @param color The css color of the cursor 33 | * @param position The row / column coordinate of the cursor marker. 34 | */ 35 | constructor(session: Ace.EditSession, 36 | cursorId: string, 37 | label: string, 38 | color: string, 39 | position: number | Ace.Point) { 40 | this._session = session; 41 | this._label = label; 42 | this._color = color; 43 | this._position = position ? this._convertPosition(position) : null; 44 | this._cursorId = cursorId; 45 | this._id = null; 46 | this._visible = false; 47 | this._tooltipTimeout = null; 48 | 49 | // Create the HTML elements 50 | this._markerElement = document.createElement("div"); 51 | this._cursorElement = document.createElement("div"); 52 | this._cursorElement.className = "ace-multi-cursor"; 53 | this._cursorElement.style.background = this._color; 54 | this._markerElement.append(this._cursorElement); 55 | 56 | this._tooltipElement = document.createElement("div"); 57 | this._tooltipElement.className = "ace-multi-cursor-tooltip"; 58 | this._tooltipElement.style.background = this._color; 59 | this._tooltipElement.style.opacity = "0"; 60 | this._tooltipElement.innerHTML = label; 61 | this._markerElement.append(this._tooltipElement); 62 | } 63 | 64 | /** 65 | * Called by Ace to update the rendering of the marker. 66 | * 67 | * @param _ The html to render, represented by an array of strings. 68 | * @param markerLayer The marker layer containing the cursor marker. 69 | * @param __ The ace edit session. 70 | * @param layerConfig 71 | */ 72 | public update(_: string[], markerLayer: any, __: Ace.EditSession, layerConfig: any): void { 73 | if (this._position === null) { 74 | return; 75 | } 76 | 77 | const screenPosition = this._session.documentToScreenPosition( 78 | this._position.row, this._position.column); 79 | 80 | const top: number = markerLayer.$getTop(screenPosition.row, layerConfig); 81 | const left: number = markerLayer.$padding + screenPosition.column * layerConfig.characterWidth; 82 | const height: number = layerConfig.lineHeight; 83 | 84 | const cursorTop = top + 2; 85 | const cursorHeight = height - 3; 86 | const cursorLeft = left; 87 | const cursorWidth = 2; 88 | 89 | this._cursorElement.style.height = `${cursorHeight}px`; 90 | this._cursorElement.style.width = `${cursorWidth}px`; 91 | this._cursorElement.style.top = `${cursorTop}px`; 92 | this._cursorElement.style.left = `${cursorLeft}px`; 93 | 94 | let toolTipTop = cursorTop - height; 95 | if (toolTipTop < 5) { 96 | toolTipTop = cursorTop + height - 1; 97 | } 98 | 99 | const toolTipLeft = cursorLeft; 100 | this._tooltipElement.style.top = `${toolTipTop - 2}px`; 101 | this._tooltipElement.style.left = `${toolTipLeft - 2}px`; 102 | 103 | // Remove the content node from whatever parent it might have now 104 | // and add it to the new parent node. 105 | this._markerElement.remove(); 106 | markerLayer.elt("remote-cursor", ""); 107 | const parentNode = markerLayer.element.childNodes[markerLayer.i - 1] || markerLayer.element.lastChild; 108 | parentNode.appendChild(this._markerElement); 109 | } 110 | 111 | /** 112 | * Sets the location of the cursor marker. 113 | * @param position The position of cursor marker. 114 | */ 115 | public setPosition(position: number | Ace.Point): void { 116 | this._position = this._convertPosition(position); 117 | this._forceSessionUpdate(); 118 | this._tooltipElement.style.opacity = "1"; 119 | this._scheduleTooltipHide(); 120 | } 121 | 122 | /** 123 | * Sets the marker to visible / invisible. 124 | * 125 | * @param visible true if the marker should be displayed, false otherwise. 126 | */ 127 | public setVisible(visible: boolean): void { 128 | const old = this._visible; 129 | 130 | this._visible = visible; 131 | if (old !== this._visible) { 132 | this._markerElement.style.visibility = visible ? "visible" : "hidden"; 133 | this._forceSessionUpdate(); 134 | } 135 | } 136 | 137 | /** 138 | * Determines if the marker should be visible. 139 | * 140 | * @returns true if the cursor should be visible, false otherwise. 141 | */ 142 | public isVisible(): boolean { 143 | return this._visible; 144 | } 145 | 146 | /** 147 | * Gets the unique id of this cursor. 148 | * @returns the unique id of this cursor. 149 | */ 150 | public cursorId(): string { 151 | return this._cursorId; 152 | } 153 | 154 | /** 155 | * Gets the id of the marker. 156 | * @returns The marker id. 157 | */ 158 | public markerId(): string { 159 | return this._id; 160 | } 161 | 162 | /** 163 | * Gets the label of the marker. 164 | * @returns The marker"s label. 165 | */ 166 | public getLabel(): string { 167 | return this._label; 168 | } 169 | 170 | private _forceSessionUpdate(): void { 171 | (this._session as any)._signal("changeFrontMarker"); 172 | } 173 | 174 | private _convertPosition(position: number | Ace.Point): Ace.Point { 175 | if (position === null) { 176 | return null; 177 | } else if (typeof position === "number") { 178 | return this._session.getDocument().indexToPosition(position, 0); 179 | } else if (typeof position.row === "number" && typeof position.column === "number") { 180 | return position; 181 | } 182 | 183 | throw new Error(`Invalid position: ${position}`); 184 | } 185 | 186 | private _scheduleTooltipHide(): void { 187 | if (this._tooltipTimeout !== null) { 188 | clearTimeout(this._tooltipTimeout); 189 | } 190 | 191 | this._tooltipTimeout = setTimeout(() => { 192 | this._tooltipElement.style.opacity = "0"; 193 | this._tooltipTimeout = null; 194 | }, 2000); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/ts/AceMultiCursorManager.ts: -------------------------------------------------------------------------------- 1 | import {Ace} from "ace-builds"; 2 | import {AceCursorMarker} from "./AceCursorMarker"; 3 | 4 | /** 5 | * Implements multiple colored cursors in the ace editor. Each cursor is 6 | * associated with a particular user. Each user is identified by a unique id 7 | * and has a color associated with them. Each cursor has a position in the 8 | * editor which is specified by a 2-d row and column ({row: 0, column: 10}). 9 | */ 10 | export class AceMultiCursorManager { 11 | 12 | private readonly _cursors: { [key: string]: AceCursorMarker }; 13 | private readonly _session: Ace.EditSession; 14 | 15 | /** 16 | * Constructs a new AceMultiCursorManager that is bound to a particular 17 | * Ace EditSession instance. 18 | * 19 | * @param session 20 | * The Ace EditSession to bind to. 21 | */ 22 | constructor(session: Ace.EditSession) { 23 | this._cursors = {}; 24 | this._session = session; 25 | } 26 | 27 | /** 28 | * Adds a new collaborative selection. 29 | * 30 | * @param id 31 | * The unique system identifier for the user associated with this selection. 32 | * @param label 33 | * A human readable / meaningful label / title that identifies the user. 34 | * @param color 35 | * A valid css color string. 36 | * @param position 37 | * A 2-d position or linear index indicating the location of the cursor. 38 | */ 39 | public addCursor(id: string, label: string, color: string, position: number | Ace.Point): void { 40 | if (this._cursors[id] !== undefined) { 41 | throw new Error(`Cursor with id already defined: ${id}`); 42 | } 43 | 44 | const marker: AceCursorMarker = new AceCursorMarker(this._session, id, label, color, position); 45 | 46 | this._cursors[id] = marker; 47 | this._session.addDynamicMarker(marker, true); 48 | } 49 | 50 | /** 51 | * Updates the selection for a particular user. 52 | * 53 | * @param id 54 | * The unique identifier for the user. 55 | * @param position 56 | * A 2-d position or linear index indicating the location of the cursor. 57 | */ 58 | public setCursor(id: string, position: number | Ace.Point): void { 59 | const cursor: AceCursorMarker = this._getCursor(id); 60 | 61 | cursor.setPosition(position); 62 | } 63 | 64 | /** 65 | * Clears the cursor (but does not remove it) for the specified user. 66 | * 67 | * @param id 68 | * The unique identifier for the user. 69 | */ 70 | public clearCursor(id: string): void { 71 | const cursor = this._getCursor(id); 72 | 73 | cursor.setPosition(null); 74 | } 75 | 76 | /** 77 | * Removes the cursor for the specified user. 78 | * 79 | * @param id 80 | * The unique identifier for the user. 81 | */ 82 | public removeCursor(id: string): void { 83 | const cursor = this._cursors[id]; 84 | 85 | if (cursor === undefined) { 86 | throw new Error(`Cursor not found: ${id}`); 87 | } 88 | // Note: ace adds an id field to all added markers. 89 | this._session.removeMarker((cursor as any).id); 90 | delete this._cursors[id]; 91 | } 92 | 93 | /** 94 | * Removes all cursors. 95 | */ 96 | public removeAll(): void { 97 | Object.getOwnPropertyNames(this._cursors).forEach((key) => { 98 | this.removeCursor(this._cursors[key].cursorId()); 99 | }); 100 | } 101 | 102 | private _getCursor(id: string): AceCursorMarker { 103 | const cursor: AceCursorMarker = this._cursors[id]; 104 | 105 | if (cursor === undefined) { 106 | throw new Error(`Cursor not found: ${id}`); 107 | } 108 | return cursor; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/ts/AceMultiSelectionManager.ts: -------------------------------------------------------------------------------- 1 | import {Ace} from "ace-builds"; 2 | import {AceSelectionMarker} from "./AceSelectionMarker"; 3 | 4 | /** 5 | * Implements multiple colored selections in the ace editor. Each selection is 6 | * associated with a particular user. Each user is identified by a unique id 7 | * and has a color associated with them. The selection manager supports block 8 | * selection through multiple AceRanges. 9 | */ 10 | export class AceMultiSelectionManager { 11 | 12 | private readonly _selections: { [key: string]: AceSelectionMarker }; 13 | private readonly _session: Ace.EditSession; 14 | 15 | /** 16 | * Constructs a new AceMultiSelectionManager that is bound to a particular 17 | * Ace EditSession instance. 18 | * 19 | * @param session 20 | * The Ace EditSession to bind to. 21 | */ 22 | constructor(session: Ace.EditSession) { 23 | this._selections = {}; 24 | this._session = session; 25 | } 26 | 27 | /** 28 | * Adds a new collaborative selection. 29 | * 30 | * @param id 31 | * The unique system identifier for the user associated with this selection. 32 | * @param label 33 | * A human readable / meaningful label / title that identifies the user. 34 | * @param color 35 | * A valid css color string. 36 | * @param ranges 37 | * An array of ace ranges that specify the initial selection. 38 | */ 39 | public addSelection(id: string, label: string, color: string, ranges: Ace.Range[]): void { 40 | if (this._selections[id] !== undefined) { 41 | throw new Error("Selection with id already defined: " + id); 42 | } 43 | 44 | const marker = new AceSelectionMarker(this._session, id, label, color, ranges); 45 | 46 | this._selections[id] = marker; 47 | this._session.addDynamicMarker(marker, false); 48 | } 49 | 50 | /** 51 | * Updates the selection for a particular user. 52 | * 53 | * @param id 54 | * The unique identifier for the user. 55 | * @param ranges 56 | * The array of ranges that specify the selection. 57 | */ 58 | public setSelection(id: string, ranges: Ace.Range[]) { 59 | const selection = this._getSelection(id); 60 | 61 | selection.setSelection(ranges); 62 | } 63 | 64 | /** 65 | * Clears the selection (but does not remove it) for the specified user. 66 | * @param id 67 | * The unique identifier for the user. 68 | */ 69 | public clearSelection(id: string): void { 70 | const selection = this._getSelection(id); 71 | 72 | selection.setSelection(null); 73 | } 74 | 75 | /** 76 | * Removes the selection for the specified user. 77 | * @param id 78 | * The unique identifier for the user. 79 | */ 80 | public removeSelection(id: string) { 81 | const selection = this._selections[id]; 82 | 83 | if (selection === undefined) { 84 | throw new Error("Selection not found: " + id); 85 | } 86 | 87 | // note: ace adds the id property to whatever marker you pass in. 88 | this._session.removeMarker((selection as any).id); 89 | delete this._selections[id]; 90 | } 91 | 92 | /** 93 | * Removes all selections. 94 | */ 95 | public removeAll(): void { 96 | Object.getOwnPropertyNames(this._selections).forEach((key) => { 97 | this.removeSelection(this._selections[key].selectionId()); 98 | }); 99 | } 100 | 101 | private _getSelection(id: string): AceSelectionMarker { 102 | const selection: AceSelectionMarker = this._selections[id]; 103 | 104 | if (selection === undefined) { 105 | throw new Error("Selection not found: " + id); 106 | } 107 | return selection; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/ts/AceRadarView.ts: -------------------------------------------------------------------------------- 1 | import {Ace} from "ace-builds"; 2 | import {AceRadarViewIndicator} from "./AceRadarViewIndicator"; 3 | import {IRowRange} from "./RowRange"; 4 | 5 | /** 6 | * Implements viewport awareness in the Ace Editor by showing where remote 7 | * users are scrolled too and where there cursor is in the document, even 8 | * if the cursor is not in view. 9 | */ 10 | export class AceRadarView { 11 | private readonly _views: { [key: string]: AceRadarViewIndicator }; 12 | private readonly _editor: Ace.Editor; 13 | private _container: HTMLElement; 14 | 15 | /** 16 | * Constructs a new AceRadarView bound to the supplied element and editor. 17 | * 18 | * @param element 19 | * The HTML Element that the AceRadarView should render to. 20 | * @param editor 21 | * The Ace Editor to listen to events from. 22 | */ 23 | constructor(element: HTMLElement | string, editor: Ace.Editor) { 24 | this._container = null; 25 | if (typeof element === "string") { 26 | this._container = document.getElementById(element); 27 | } else { 28 | this._container = element; 29 | } 30 | 31 | this._container.style.position = "relative"; 32 | this._views = {}; 33 | this._editor = editor; 34 | } 35 | 36 | /** 37 | * Add a view indicator for a new remote user. 38 | * 39 | * @param id 40 | * The unique id of the user. 41 | * @param label 42 | * A text label to displAce for the user. 43 | * @param color 44 | * The color to render the indicator with. 45 | * @param viewRows 46 | * The rows the user's viewport spans. 47 | * @param cursorRow 48 | * The row that the user's cursor is on. 49 | */ 50 | public addView(id: string, label: string, color: string, viewRows: IRowRange, cursorRow: number) { 51 | const indicator = new AceRadarViewIndicator( 52 | label, 53 | color, 54 | viewRows, 55 | cursorRow, 56 | this._editor 57 | ); 58 | 59 | this._container.appendChild(indicator.element()); 60 | indicator.update(); 61 | 62 | this._views[id] = indicator; 63 | } 64 | 65 | /** 66 | * Determines if the AceRadarView has an indicator for this specified user. 67 | * 68 | * @param id 69 | * The id of the user to check for. 70 | * @returns 71 | * True if the AceRadarView has an indicator for this user, false otherwise. 72 | */ 73 | public hasView(id: string): boolean { 74 | return this._views[id] !== undefined; 75 | } 76 | 77 | /** 78 | * Sets the view row span for a particular user. 79 | * 80 | * @param id 81 | * The id of the user to set the rows for. 82 | * @param rows 83 | * The row range to set. 84 | */ 85 | public setViewRows(id: string, rows: IRowRange) { 86 | const indicator = this._views[id]; 87 | indicator.setViewRows(rows); 88 | } 89 | 90 | /** 91 | * Sets the cursor row for a particular user. 92 | * 93 | * @param id 94 | * The id of the user to set the cursor row for. 95 | * @param row 96 | * The row to set. 97 | */ 98 | public setCursorRow(id: string, row: number) { 99 | const indicator = this._views[id]; 100 | indicator.setCursorRow(row); 101 | } 102 | 103 | /** 104 | * Clears the view for a particular user, causing their indicator to disapear. 105 | * @param id 106 | * The id of the user to clear. 107 | */ 108 | public clearView(id: string): void { 109 | const indicator = this._views[id]; 110 | indicator.setCursorRow(null); 111 | indicator.setViewRows(null); 112 | } 113 | 114 | /** 115 | * Removes the view indicator for the specified user. 116 | * @param id 117 | * The id of the user to remove the view indicator for. 118 | */ 119 | public removeView(id: string): void { 120 | const indicator = this._views[id]; 121 | indicator.dispose(); 122 | delete this._views[id]; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/ts/AceRadarViewIndicator.ts: -------------------------------------------------------------------------------- 1 | import {Ace} from "ace-builds"; 2 | import {IRowRange} from "./RowRange"; 3 | 4 | export class AceRadarViewIndicator { 5 | 6 | private readonly _label: string; 7 | private readonly _color: string; 8 | private readonly _editorListener: () => void; 9 | private readonly _scrollElement: HTMLDivElement; 10 | private readonly _cursorElement: HTMLDivElement; 11 | private readonly _wrapper: HTMLDivElement; 12 | private _viewRows: IRowRange; 13 | private _cursorRow: number; 14 | private _editor: Ace.Editor; 15 | private _docLineCount: number; 16 | 17 | constructor(label: string, color: string, viewRows: IRowRange, cursorRow: number, editor: Ace.Editor) { 18 | this._label = label; 19 | this._color = color; 20 | this._viewRows = viewRows; 21 | this._cursorRow = cursorRow; 22 | this._editor = editor; 23 | this._docLineCount = editor.getSession().getLength(); 24 | 25 | this._editorListener = () => { 26 | const newLineCount = this._editor.getSession().getLength(); 27 | 28 | if (newLineCount !== this._docLineCount) { 29 | this._docLineCount = newLineCount; 30 | this.update(); 31 | } 32 | }; 33 | this._editor.on("change", this._editorListener); 34 | 35 | this._scrollElement = document.createElement("div"); 36 | this._scrollElement.className = "ace-radar-view-scroll-indicator"; 37 | 38 | this._scrollElement.style.borderColor = this._color; 39 | this._scrollElement.style.background = this._color; 40 | 41 | // todo implement a custom tooltip for consistent presentation. 42 | this._scrollElement.title = this._label; 43 | 44 | this._scrollElement.addEventListener("click", () => { 45 | const middle = ((this._viewRows.end - this._viewRows.start) / 2) + this._viewRows.start; 46 | 47 | this._editor.scrollToLine(middle, true, false, () => { /* no-op */ 48 | }); 49 | }, false); 50 | 51 | this._cursorElement = document.createElement("div"); 52 | this._cursorElement.className = "ace-radar-view-cursor-indicator"; 53 | this._cursorElement.style.background = this._color; 54 | this._cursorElement.title = this._label; 55 | 56 | this._cursorElement.addEventListener("click", () => { 57 | this._editor.scrollToLine(this._cursorRow, true, false, () => { /* no-op */ 58 | }); 59 | }, false); 60 | 61 | this._wrapper = document.createElement("div"); 62 | this._wrapper.className = "ace-radar-view-wrapper"; 63 | this._wrapper.style.display = "none"; 64 | 65 | this._wrapper.appendChild(this._scrollElement); 66 | this._wrapper.appendChild(this._cursorElement); 67 | } 68 | 69 | public element(): HTMLDivElement { 70 | return this._wrapper; 71 | } 72 | 73 | public setCursorRow(cursorRow: number): void { 74 | this._cursorRow = cursorRow; 75 | this.update(); 76 | } 77 | 78 | public setViewRows(viewRows: IRowRange): void { 79 | this._viewRows = viewRows; 80 | this.update(); 81 | } 82 | 83 | public update(): void { 84 | if (!_isSet(this._viewRows) && !_isSet(this._cursorRow)) { 85 | this._wrapper.style.display = "none"; 86 | } else { 87 | this._wrapper.style.display = null; 88 | const maxLine = this._docLineCount - 1; 89 | 90 | if (!_isSet(this._viewRows)) { 91 | this._scrollElement.style.display = "none"; 92 | } else { 93 | const topPercent = Math.min(maxLine, this._viewRows.start) / maxLine * 100; 94 | const bottomPercent = 100 - (Math.min(maxLine, this._viewRows.end) / maxLine * 100); 95 | 96 | this._scrollElement.style.top = topPercent + "%"; 97 | this._scrollElement.style.bottom = bottomPercent + "%"; 98 | this._scrollElement.style.display = null; 99 | } 100 | 101 | if (!_isSet(this._cursorRow)) { 102 | this._cursorElement.style.display = "none"; 103 | } else { 104 | const cursorPercent = Math.min(this._cursorRow, maxLine) / maxLine; 105 | const ratio = (this._wrapper.offsetHeight - this._cursorElement.offsetHeight) / this._wrapper.offsetHeight; 106 | const cursorTop = cursorPercent * ratio * 100; 107 | 108 | this._cursorElement.style.top = cursorTop + "%"; 109 | this._cursorElement.style.display = null; 110 | } 111 | } 112 | } 113 | 114 | public dispose(): void { 115 | this._wrapper.parentNode.removeChild(this._wrapper); 116 | this._editor.off("change", this._editorListener); 117 | } 118 | } 119 | 120 | function _isSet(value: any): boolean { 121 | return value !== undefined && value !== null; 122 | } 123 | -------------------------------------------------------------------------------- /src/ts/AceRangeUtil.ts: -------------------------------------------------------------------------------- 1 | import {Ace, Range} from "ace-builds"; 2 | 3 | export interface IRangeData { 4 | start: {row: number, column: number}; 5 | end: {row: number, column: number}; 6 | } 7 | 8 | /** 9 | * A helper class for working with Ace Ranges. 10 | */ 11 | export class AceRangeUtil { 12 | 13 | public static rangeToJson(range: Ace.Range): IRangeData { 14 | return { 15 | start: { 16 | row: range.start.row, 17 | column: range.start.column 18 | }, 19 | end: { 20 | row: range.end.row, 21 | column: range.end.column 22 | } 23 | }; 24 | } 25 | 26 | public static jsonToRange(range: IRangeData): Ace.Range { 27 | return new Range( 28 | range.start.row, 29 | range.start.column, 30 | range.end.row, 31 | range.end.column); 32 | } 33 | 34 | public static rangesToJson(ranges: Ace.Range[]): IRangeData[] { 35 | return ranges.map((range) => { 36 | return AceRangeUtil.rangeToJson(range); 37 | }); 38 | } 39 | 40 | public static jsonToRanges(ranges: IRangeData[]): Ace.Range[] { 41 | return ranges.map((range) => { 42 | return AceRangeUtil.jsonToRange(range); 43 | }); 44 | } 45 | 46 | public static toJson(value: Ace.Range): IRangeData; 47 | public static toJson(value: Ace.Range[]): IRangeData[]; 48 | public static toJson(value: Ace.Range | Ace.Range[]) { 49 | if (Array.isArray(value)) { 50 | return AceRangeUtil.rangesToJson(value); 51 | } 52 | 53 | return AceRangeUtil.rangeToJson(value); 54 | } 55 | 56 | public static fromJson(value: IRangeData): Ace.Range; 57 | public static fromJson(value: IRangeData[]): Ace.Range[]; 58 | public static fromJson(value: IRangeData | IRangeData[]): Ace.Range | Ace.Range[] { 59 | if (Array.isArray(value)) { 60 | return AceRangeUtil.jsonToRanges(value); 61 | } 62 | 63 | return AceRangeUtil.jsonToRange(value); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/ts/AceSelectionMarker.ts: -------------------------------------------------------------------------------- 1 | import {Ace} from "ace-builds"; 2 | 3 | export interface ISelectionBounds { 4 | height?: number; 5 | width?: number; 6 | top?: number; 7 | left?: number; 8 | bottom?: number; 9 | right?: number; 10 | } 11 | 12 | export class AceSelectionMarker implements Ace.MarkerLike { 13 | public range: Ace.Range; 14 | public type: string; 15 | public renderer?: Ace.MarkerRenderer; 16 | public clazz: string; 17 | public inFront: boolean; 18 | public id: number; 19 | 20 | private _session: Ace.EditSession; 21 | private readonly _label: string; 22 | private readonly _color: string; 23 | private _ranges: Ace.Range[]; 24 | private readonly _selectionId: string; 25 | private readonly _id: string; 26 | private readonly _markerElement: HTMLDivElement; 27 | 28 | constructor(session: Ace.EditSession, selectionId: string, label: string, color: string, ranges: Ace.Range[]) { 29 | this._session = session; 30 | this._label = label; 31 | this._color = color; 32 | this._ranges = ranges || []; 33 | this._selectionId = selectionId; 34 | this._id = null; 35 | this._markerElement = document.createElement("div"); 36 | } 37 | 38 | public update(_: string[], markerLayer: any, session: Ace.EditSession, layerConfig: any): void { 39 | while (this._markerElement.hasChildNodes()) { 40 | this._markerElement.removeChild(this._markerElement.lastChild); 41 | } 42 | 43 | this._ranges.forEach((range) => { 44 | this._renderRange(markerLayer, session, layerConfig, range); 45 | }); 46 | 47 | this._markerElement.remove(); 48 | markerLayer.elt("remote-selection", ""); 49 | const parentNode = markerLayer.element.childNodes[markerLayer.i - 1] || markerLayer.element.lastChild; 50 | parentNode.appendChild(this._markerElement); 51 | } 52 | 53 | public setSelection(ranges: Ace.Range[]): void { 54 | if (ranges === undefined || ranges === null) { 55 | this._ranges = []; 56 | } else if (ranges instanceof Array) { 57 | this._ranges = ranges; 58 | } else { 59 | this._ranges = [ranges]; 60 | } 61 | 62 | this._forceSessionUpdate(); 63 | } 64 | 65 | public getLabel(): string { 66 | return this._label; 67 | } 68 | 69 | public selectionId(): string { 70 | return this._selectionId; 71 | } 72 | 73 | public markerId(): string { 74 | return this._id; 75 | } 76 | 77 | private _renderLine(bounds: ISelectionBounds): void { 78 | const div = document.createElement("div"); 79 | div.className = "ace-multi-selection"; 80 | div.style.backgroundColor = this._color; 81 | 82 | if (typeof bounds.height === "number") { 83 | div.style.height = `${bounds.height}px`; 84 | } 85 | 86 | if (typeof bounds.width === "number") { 87 | div.style.width = `${bounds.width}px`; 88 | } 89 | 90 | if (typeof bounds.top === "number") { 91 | div.style.top = `${bounds.top}px`; 92 | } 93 | 94 | if (typeof bounds.left === "number") { 95 | div.style.left = `${bounds.left}px`; 96 | } 97 | 98 | if (typeof bounds.bottom === "number") { 99 | div.style.bottom = `${bounds.bottom}px`; 100 | } 101 | 102 | if (typeof bounds.right === "number") { 103 | div.style.right = `${bounds.right}px`; 104 | } 105 | 106 | this._markerElement.append(div); 107 | } 108 | 109 | private _renderRange(markerLayer: any, session: Ace.EditSession, layerConfig: any, range: Ace.Range): void { 110 | const screenRange: Ace.Range = range.toScreenRange(session); 111 | 112 | let height: number = layerConfig.lineHeight; 113 | let top: number = markerLayer.$getTop(screenRange.start.row, layerConfig); 114 | let width: number = 0; 115 | const right = 0; 116 | const left: number = markerLayer.$padding + screenRange.start.column * layerConfig.characterWidth; 117 | 118 | if (screenRange.isMultiLine()) { 119 | // Render the start line 120 | this._renderLine({height, right, top, left}); 121 | 122 | // from start of the last line to the selection end 123 | top = markerLayer.$getTop(screenRange.end.row, layerConfig); 124 | width = screenRange.end.column * layerConfig.characterWidth; 125 | this._renderLine({height, width, top, left: markerLayer.$padding}); 126 | 127 | // all the complete lines 128 | height = (screenRange.end.row - screenRange.start.row - 1) * layerConfig.lineHeight; 129 | if (height < 0) { 130 | return; 131 | } 132 | top = markerLayer.$getTop(screenRange.start.row + 1, layerConfig); 133 | this._renderLine({height, right, top, left: markerLayer.$padding}); 134 | } else { 135 | width = (range.end.column - range.start.column) * layerConfig.characterWidth; 136 | this._renderLine({height, width, top, left}); 137 | } 138 | } 139 | 140 | private _forceSessionUpdate(): void { 141 | (this._session as any)._signal("changeBackMarker"); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/ts/AceViewportUtil.ts: -------------------------------------------------------------------------------- 1 | import {Ace} from "ace-builds"; 2 | import {IIndexRange} from "./IndexRange"; 3 | import {IRowRange} from "./RowRange"; 4 | 5 | export class AceViewportUtil { 6 | 7 | public static getVisibleIndexRange(editor: Ace.Editor): IIndexRange { 8 | let firstRow: number = editor.getFirstVisibleRow(); 9 | let lastRow: number = editor.getLastVisibleRow(); 10 | 11 | if (!editor.isRowFullyVisible(firstRow)) { 12 | firstRow++; 13 | } 14 | 15 | if (!editor.isRowFullyVisible(lastRow)) { 16 | lastRow--; 17 | } 18 | 19 | const startPos: number = editor.getSession().getDocument().positionToIndex({row: firstRow, column: 0}, 0); 20 | 21 | // todo, this should probably be the end of the row 22 | const endPos: number = editor.getSession().getDocument().positionToIndex({row: lastRow, column: 0}, 0); 23 | 24 | return { 25 | start: startPos, 26 | end: endPos 27 | }; 28 | } 29 | 30 | public static indicesToRows(editor: Ace.Editor, startIndex: number, endIndex: number): IRowRange { 31 | const startRow: number = editor.getSession().getDocument().indexToPosition(startIndex, 0).row; 32 | const endRow: number = editor.getSession().getDocument().indexToPosition(endIndex, 0).row; 33 | 34 | return { 35 | start: startRow, 36 | end: endRow 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ts/IndexRange.ts: -------------------------------------------------------------------------------- 1 | export interface IIndexRange { 2 | start: number; 3 | end: number; 4 | } 5 | -------------------------------------------------------------------------------- /src/ts/RowRange.ts: -------------------------------------------------------------------------------- 1 | export interface IRowRange { 2 | start: number; 3 | end: number; 4 | } 5 | -------------------------------------------------------------------------------- /src/ts/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./AceMultiSelectionManager"; 2 | export * from "./AceMultiCursorManager"; 3 | export * from "./AceRangeUtil"; 4 | export * from "./AceRadarView"; 5 | export * from "./AceViewportUtil"; 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "ES2020", 5 | "outDir": "dist", 6 | "preserveConstEnums": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "declaration": false, 10 | "esModuleInterop": true, 11 | "sourceMap": true, 12 | "allowSyntheticDefaultImports": true, 13 | "inlineSources": true, 14 | "baseUrl": "./src/ts", 15 | "lib": ["dom", "es6", "es7"] 16 | }, 17 | "include": [ 18 | "src/**/*" 19 | ], 20 | "exclude": [ 21 | "build", 22 | "dist", 23 | "example" 24 | ] 25 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "object-literal-sort-keys": false, 5 | "trailing-comma": false, 6 | "variable-name": false 7 | } 8 | } --------------------------------------------------------------------------------