diff --git a/CHANGELOG b/CHANGELOG
index 206c168..9e804bd 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,80 @@
+Webglimpse 2.6.0 (release 2022-12-7)
+* Added ability to specify min/max limits on TimeAxis1D / Axis1D.
+* Fix warnings about adding `willReadFrequently` option to canvas context that
+ calls `getImageData` a lot.
+
+Webglimpse 2.5.0 (release 2020-11-18)
+* Added annotation label backgrounds.
+
+Webglimpse 2.4.2 (release 2020-7-27)
+* Fixed a bug with the insertion index of an OrderedSet when moveIfExists is
+ set to true.
+
+Webglimpse 2.4.1 (release 2020-7-20)
+
+* Fixed a bug when scrolling the mouse wheel over the time selector when using
+ a custom mouse wheel listener.
+
+Webglimpse 2.4.0 (release 2020-6-26)
+
+* Added ability to highlight a row, similar to group highlight.
+* By default selecting an event no longer changes the border color unless
+ the `selectedBorderColor` property is set on the `newEventBarsPainterFactory`
+ style.
+
+Webglimpse 2.3.0 (release 2019-11-8)
+
+* Added ability to disable picking of timeline annotations. Set `picking` to false.
+
+Webglimpse 2.2.0 (release 2019-6-19)
+
+* Added ability to specify custom mouse cursor for TimelineRow and TimelineGroup.
+* Updated Typescript to ~3.4.5 and other dev dependencies.
+* Replaced deprecated MouseWheelEvent listener with WheelEvent listener.
+
+Webglimpse 2.1.1 (release 2019-4-16)
+
+* Added support for truncating label text with ellipsis.
+* Added support for changing row and group label fonts.
+
+Webglimpse 2.0.7 (release 2019-2-8)
+
+* Re-structure groups, so they are in a container pane.
+* Make group highlight extend to the height of the group instead of just the label.
+* Add ability to make group highlight dashed.
+* Make group highlight properties configurable per group.
+
+Webglimpse 2.0.6 (release 2018-7-12)
+
+* Truncate event start/end times, so they are never sub-millisecond.
+
+Webglimpse 2.0.5 (release 2018-4-12)
+
+* Fixed a missing moment import.
+
+Webglimpse 2.0.4 (release 2018-4-11)
+
+* Fixed some issues with variables being re-declared in if block scopes
+ that was causing the code to not generate properly.
+* Added tslint.json and fixed lots of tslint errors.
+
+Webglimpse 2.0.3 (release 2018-3-29)
+
+* Fixed incorrect scoped redefinition of variables.
+
+Webglimpse 2.0.2 (release 2018-3-28)
+
+* Replaced build system with ng-packagr.
+* Updated typescript and other dependencies.
+* Fixed tons of typescript errors.
+* Removed Webglimpse namespace and ported code to use newer module based structure.
+
+Webglimpse 1.9.2 (released 2018-3-22)
+
+* Made release package installable from npm.
+* Add ability to modify the formats of absolute time display with `timeAxisFormat`.
+* Added ability to highlight a group with `highlighted` and `groupHighlightColor`.
+
Webglimpse 1.9.1 (released 2016-11-22)
* Allow truncation of event text (feature intended for 1.9.0 but inadvertently left out)
@@ -28,7 +105,7 @@ Webglimpse 1.7.2-pre8 (released 2016-7-15)
* Fix bug with maximized rows which broke y axis scrolling after unmaximizing.
* Add TimelinePaneOptions.axisLabelAlign to control how date labels are centered relative to the start/end of the day on timeline.
* Add TimelinePaneOptions.mouseWheelListener to provide an alternative listener to handle mouse wheel events on timeline.
-* Add TimelinePaneOptions.centerSelectedIntervalOnDoubleClick to control whether timeline selection centers on double clicks.
+* Add TimelinePaneOptions.centerSelectedIntervalOnDoubleClick to control whether timeline selection centers on double clicks.
Webglimpse 1.7.2-pre7 (released 2016-7-8)
@@ -76,9 +153,9 @@ Webglimpse 1.7.0 (released 2016-4-26)
Webglimpse 1.7.0-pre4 (released 2016-3-12)
- * Lock row. Rows can be locked to the top or the bottom of the timeline so
+ * Lock row. Rows can be locked to the top or the bottom of the timeline so
that they are always visible.
- * Maximize row. A set of rows can be temporarily set to maximized. In that
+ * Maximize row. A set of rows can be temporarily set to maximized. In that
mode they take up all available timeline space.
* Ability to customize individual row heights.
* Fix bug with 0 length events when event overlaps are allowed.
diff --git a/Gruntfile.js b/Gruntfile.js
deleted file mode 100644
index 64f2993..0000000
--- a/Gruntfile.js
+++ /dev/null
@@ -1,378 +0,0 @@
-'use strict';
-
-module.exports = function ( grunt ) {
-
- var _ = require( 'lodash' );
-
- var licenseText = grunt.file.read( 'HEADER' ).replace( /\n$/, '' );
- var sourceHeader = '/*\n' + licenseText.replace( /^/mg, ' * ' ).replace( / *$/mg, '' ) + '\n */';
- var mungedSourceHeader = '\n' + licenseText.replace( /^/mg, '* ' ).replace( / *$/mg, '' ) + '\n';
-
- var releaseVersion = '1.9.1';
- var releaseDate = grunt.template.today( 'UTC:yyyy-mm-dd HH:MM:ss Z' );
- var releaseText = [
- 'Web Glimpse v' + releaseVersion,
- 'Released: ' + releaseDate,
- 'https://glimpse.metsci.com/webglimpse/',
- ''
- ].join( '\n' );
- var releaseHeader = '/*!\n' + ( releaseText + '\n' + licenseText ).replace( /^/mg, ' * ' ).replace( / *$/mg, '' ) + '\n */';
-
-
-
- //
- // Common
- //
-
- var commonConfig = {
-
- ts: {
- options: {
- target: 'es5',
- comments: true,
- sourceRoot: '.'
- }
- },
-
- clean: {
- build: [ 'build/grunt' ],
- misc: [ 'tscommand*.tmp.txt' ]
- }
-
- };
-
- var commonTasks = { };
-
-
-
- //
- // Dev
- //
-
- var devConfig = {
-
- ts: {
- dev: {
- src: [ 'src/**/*.ts' ],
- reference: 'src/reference.ts',
- out: 'build/grunt/dev/webglimpse-dev.js'
- }
- },
-
- watch: {
- dev: {
- files: [ 'src/**/*.ts' ],
- tasks: [ 'ts:dev' ]
- }
- },
-
- connect: {
- dev: {
- options: {
- hostname: 'localhost',
- port: 8000,
- base: [ 'build/grunt/dev', 'src' ]
- }
- }
- },
-
- clean: {
- dev: [ 'build/grunt/dev' ]
- }
-
- };
-
- var devTasks = {
- build: [
- 'ts:dev'
- ],
- serve: [
- 'build',
- 'connect:dev',
- 'watch:dev'
- ]
- };
-
-
-
- //
- // Release
- //
-
- var releaseConfig = {
-
- copy: {
- release_sources: {
- expand: true,
- cwd: 'src/',
- src: [ '**' ],
- dest: 'build/grunt/release/'
- },
- release_license: {
- src: 'LICENSE',
- dest: 'build/grunt/release/'
- },
- release_changelog: {
- src: 'CHANGELOG',
- dest: 'build/grunt/release/'
- },
- release_webglimpse_defs: {
- expand: true,
- cwd: 'build/grunt/release/tmp/',
- src: 'webglimpse.d.ts',
- dest: 'build/grunt/release/'
- }
- },
-
- ts: {
- release_webglimpse: {
- src: [ 'build/grunt/release/webglimpse/**/*.ts' ],
- reference: 'build/grunt/release/reference.ts',
- out: 'build/grunt/release/tmp/webglimpse.js',
- options: {
- declaration: true
- }
- },
- release_plot_example: {
- src: [
- 'build/grunt/release/webglimpse/defs/**/*.d.ts',
- 'build/grunt/release/webglimpse.d.ts',
- 'build/grunt/release/examples/defs/**/*.d.ts',
- 'build/grunt/release/examples/plot/**/*.ts'
- ],
- outDir: 'build/grunt/release/examples/plot/tmp/'
- },
- release_scroll_example: {
- src: [
- 'build/grunt/release/webglimpse/defs/**/*.d.ts',
- 'build/grunt/release/webglimpse.d.ts',
- 'build/grunt/release/examples/defs/**/*.d.ts',
- 'build/grunt/release/examples/scroll/**/*.ts'
- ],
- outDir: 'build/grunt/release/examples/scroll/tmp/'
- },
- release_timeline_example: {
- src: [
- 'build/grunt/release/webglimpse/defs/**/*.d.ts',
- 'build/grunt/release/webglimpse.d.ts',
- 'build/grunt/release/examples/defs/**/*.d.ts',
- 'build/grunt/release/examples/timeline/**/*.ts'
- ],
- outDir: 'build/grunt/release/examples/timeline/tmp/'
- }
- },
-
- uglify: {
- release_webglimpse: {
- options: {
- mangle: false,
- compress: false,
- beautify: true,
- preserveComments: function( node, comment ) {
- return ( comment.value !== mungedSourceHeader );
- },
- banner: releaseHeader,
- sourceMap: true,
- sourceMapIn: 'build/grunt/release/tmp/webglimpse.js.map'
- },
- files: {
- 'build/grunt/release/webglimpse.js': [ 'build/grunt/release/tmp/webglimpse.js' ]
- }
- },
- release_plot_example: {
- options: {
- mangle: false,
- compress: false,
- beautify: true,
- preserveComments: function( node, comment ) {
- return ( comment.value !== mungedSourceHeader );
- },
- banner: releaseHeader,
- sourceMap: true,
- sourceMapIn: 'build/grunt/release/examples/plot/tmp/plot.js.map'
- },
- files: {
- 'build/grunt/release/examples/plot/plot.js': [ 'build/grunt/release/examples/plot/tmp/plot.js' ]
- }
- },
- release_scroll_example: {
- options: {
- mangle: false,
- compress: false,
- beautify: true,
- preserveComments: function( node, comment ) {
- return ( comment.value !== mungedSourceHeader );
- },
- banner: releaseHeader,
- sourceMap: true,
- sourceMapIn: 'build/grunt/release/examples/scroll/tmp/scroll.js.map'
- },
- files: {
- 'build/grunt/release/examples/scroll/scroll.js': [ 'build/grunt/release/examples/scroll/tmp/scroll.js' ]
- }
- },
- release_timeline_example: {
- options: {
- mangle: false,
- compress: false,
- beautify: true,
- preserveComments: function( node, comment ) {
- return ( comment.value !== mungedSourceHeader );
- },
- banner: releaseHeader,
- sourceMap: true,
- sourceMapIn: 'build/grunt/release/examples/timeline/tmp/timeline.js.map'
- },
- files: {
- 'build/grunt/release/examples/timeline/timeline.js': [ 'build/grunt/release/examples/timeline/tmp/timeline.js' ]
- }
- }
- },
-
- replace: {
- release_reference: {
- // Remove examples from reference.ts
- src: [ 'build/grunt/release/reference.ts' ],
- dest: 'build/grunt/release/reference.ts',
- replacements: [ {
- from: /\/\/\/\s*/g,
- to: ''
- }, {
- from: /\/\/\s*Examples/g,
- to: ''
- } ]
- },
- release_html: {
- src: [ 'build/grunt/release/**/*.html' ],
- overwrite: true,
- replacements: [ {
- from: //g,
- to: '/g,
- to: '-->'
- }, {
- from: /'
- }, {
- from: /-->/g,
- to: ''
- } ]
- },
- release_webglimpse_defs: {
- // Remove ref tags from webglimpse.d.ts
- src: [ 'build/grunt/release/webglimpse.d.ts' ],
- overwrite: true,
- replacements: [ {
- from: /\/\/\/\s*/g,
- to: ''
- } ]
- }
- },
-
- connect: {
- release: {
- options: {
- hostname: 'localhost',
- port: 8001,
- base: [ 'build/grunt/release' ],
- keepalive: true
- }
- }
- },
-
- clean: {
- release_webglimpse_tmp: [ 'build/grunt/release/tmp' ],
- release_plot_example_tmp: [ 'build/grunt/release/examples/plot/tmp' ],
- release_scroll_example_tmp: [ 'build/grunt/release/examples/scroll/tmp' ],
- release_timeline_example_tmp: [ 'build/grunt/release/examples/timeline/tmp' ],
- release: [ 'build/grunt/release' ]
- }
-
- };
-
- var releaseTasks = {
- release: [
- // Clean
- 'clean:release',
- // Copy all, and modify copies
- 'copy:release_sources',
- 'copy:release_license',
- 'copy:release_changelog',
- 'replace:release_reference',
- // Compile webglimpse, and modify generated
- 'ts:release_webglimpse',
- 'uglify:release_webglimpse',
- 'copy:release_webglimpse_defs',
- 'replace:release_webglimpse_defs',
- 'clean:release_webglimpse_tmp',
- // Compile examples, and modify generated
- 'ts:release_plot_example',
- 'ts:release_scroll_example',
- 'ts:release_timeline_example',
- 'uglify:release_plot_example',
- 'uglify:release_scroll_example',
- 'uglify:release_timeline_example',
- 'clean:release_plot_example_tmp',
- 'clean:release_scroll_example_tmp',
- 'clean:release_timeline_example_tmp',
- 'replace:release_html'
- ],
- serve_release: [
- 'release',
- 'connect:release'
- ]
- };
-
-
-
- //
- // Util
- //
-
- var utilConfig = {
-
- copy: {
- prepend_source_headers: {
- cwd: 'src/',
- src: [ '**/*.ts', '!**/*.d.ts', '!reference.ts' ],
- dest: 'src/',
- expand: true,
- options: {
- process: function( content ) {
- var hasHeader = ( content.substring( 0, sourceHeader.length ) === sourceHeader );
- return ( hasHeader ? content : sourceHeader + '\n' + content );
- }
- }
- }
- }
-
- };
-
- var utilTasks = {
- add_headers: [
- 'copy:prepend_source_headers'
- ]
- };
-
-
-
- //
- // Grunt
- //
-
- require( 'load-grunt-tasks' )( grunt );
-
- grunt.initConfig( _.merge( commonConfig, devConfig, releaseConfig, utilConfig ) );
-
- var tasks = _.merge( commonTasks, devTasks, releaseTasks, utilTasks );
- for ( var taskName in tasks ) {
- if ( tasks.hasOwnProperty( taskName ) ) {
- grunt.registerTask( taskName, tasks[ taskName ] );
- }
- }
-
- grunt.registerTask( 'default', [ 'build' ] );
-
-};
-
diff --git a/README.md b/README.md
index 08e3c77..7eb1874 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,17 @@
Webglimpse is a data visualization library for the web.
+## Installation
+
+To install the Webglimpse library, run the following command.
+
+`npm install --save webglimpse`
+
+## Usage
+
+To use Webglimpse, import into your typescript project.
+
+`import * as Webglimpse from 'webglimpse'`
## WebGL
diff --git a/ng-package.json b/ng-package.json
new file mode 100644
index 0000000..7604aa6
--- /dev/null
+++ b/ng-package.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "./node_modules/ng-packagr/ng-package.schema.json",
+ "lib": {
+ "entryFile": "src/public_api.ts"
+ },
+ "allowedNonPeerDependencies": [
+ "moment"
+ ]
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..d78054c
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,9078 @@
+{
+ "name": "webglimpse",
+ "version": "2.6.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@aduh95/viz.js": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/@aduh95/viz.js/-/viz.js-3.7.0.tgz",
+ "integrity": "sha512-20Pk2Z98fbPLkECcrZSJszKos/OgtvJJR3NcbVfgCJ6EQjDNzW2P1BKqImOz3tJ952dvO2DWEhcLhQ1Wz1e9ng==",
+ "dev": true
+ },
+ "@ampproject/remapping": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
+ "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/gen-mapping": "^0.1.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "dependencies": {
+ "@jridgewell/gen-mapping": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
+ "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.0",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ }
+ }
+ },
+ "@angular-devkit/architect": {
+ "version": "0.1500.2",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1500.2.tgz",
+ "integrity": "sha512-gprTEy6vD57yJCd6JqRaFZ/pfmXuEDHlH7mOVBcBdTGuuE1VJkiNJ69E9gw552L7wwVEsF0D6lVYTFHVDkMDmA==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "15.0.2",
+ "rxjs": "6.6.7"
+ },
+ "dependencies": {
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ }
+ }
+ },
+ "@angular-devkit/core": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.0.2.tgz",
+ "integrity": "sha512-XkdNKyeYvnCq0zWuEda163muUV38ifNyK6EjrbhI5pQWNg7myxtDmLenjtpXLGLZwjxqsart2l/uoYsk1xQCmQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "8.11.0",
+ "ajv-formats": "2.1.1",
+ "jsonc-parser": "3.2.0",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.4"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ }
+ }
+ },
+ "@angular-devkit/schematics": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.0.2.tgz",
+ "integrity": "sha512-FqimOzQCwRGmz/NPPiK6QpAxI+qpy8rYQRlWRWSOfQgjb+cl4rD2isllalYvzO+5mmb88fwJ7bj9cP2X5khpcg==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "15.0.2",
+ "jsonc-parser": "3.2.0",
+ "magic-string": "0.26.7",
+ "ora": "5.4.1",
+ "rxjs": "6.6.7"
+ },
+ "dependencies": {
+ "rxjs": {
+ "version": "6.6.7",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+ "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ }
+ }
+ },
+ "@angular/cli": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-15.0.2.tgz",
+ "integrity": "sha512-SRPCYiK5A+U0V173EUZNYHtOvFtxefH9hfie2+XcJX9B/7VxqIkyRYXOUUyWtC7yzbxWNJ1+WG5X4S5C2LrTEw==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/architect": "0.1500.2",
+ "@angular-devkit/core": "15.0.2",
+ "@angular-devkit/schematics": "15.0.2",
+ "@schematics/angular": "15.0.2",
+ "@yarnpkg/lockfile": "1.1.0",
+ "ansi-colors": "4.1.3",
+ "ini": "3.0.1",
+ "inquirer": "8.2.4",
+ "jsonc-parser": "3.2.0",
+ "npm-package-arg": "9.1.2",
+ "npm-pick-manifest": "8.0.1",
+ "open": "8.4.0",
+ "ora": "5.4.1",
+ "pacote": "15.0.6",
+ "resolve": "1.22.1",
+ "semver": "7.3.8",
+ "symbol-observable": "4.0.0",
+ "yargs": "17.6.2"
+ },
+ "dependencies": {
+ "ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true
+ },
+ "ini": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz",
+ "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==",
+ "dev": true
+ }
+ }
+ },
+ "@angular/common": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@angular/common/-/common-15.0.2.tgz",
+ "integrity": "sha512-BZkaUdFv6A0a8m3R/HEYmwZrULMHwRWQh+ukSQEz9reVNUiH0/mNvj9I4iYYCfYXHPiyMIRfAeu/fqdl14DBFg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.3.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==",
+ "dev": true
+ }
+ }
+ },
+ "@angular/compiler": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-15.0.2.tgz",
+ "integrity": "sha512-dD1Vv2txp09V7RaJK2tvGRWpM2RsDARVAGL65hfJY2txeEFRyajcle6jOI11NfGpgsZvptN7o5oc8ozQI1hgTA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.3.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==",
+ "dev": true
+ }
+ }
+ },
+ "@angular/compiler-cli": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-15.0.2.tgz",
+ "integrity": "sha512-WZCULskbuoMA0iQ36H2nGgKj017JENR+wDnwuQ+/1ECtBN2PpjJiws/m5bntBzQe96w2fue6tOLh09WESQgRHw==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.17.2",
+ "chokidar": "^3.0.0",
+ "convert-source-map": "^1.5.1",
+ "dependency-graph": "^0.11.0",
+ "magic-string": "^0.26.0",
+ "reflect-metadata": "^0.1.2",
+ "semver": "^7.0.0",
+ "sourcemap-codec": "^1.4.8",
+ "tslib": "^2.3.0",
+ "yargs": "^17.2.1"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==",
+ "dev": true
+ }
+ }
+ },
+ "@angular/core": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-15.0.2.tgz",
+ "integrity": "sha512-qM0T4r3Z3Qfk+5+M5JRB1qr3AUKgabbC0kARY+R+QvwbQBE6UoeWw5eRuUbTGDP70Ikeeg0xZebpkr7Oghp9/A==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.3.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/code-frame": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
+ "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.18.6"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz",
+ "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.5.tgz",
+ "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==",
+ "dev": true,
+ "requires": {
+ "@ampproject/remapping": "^2.1.0",
+ "@babel/code-frame": "^7.18.6",
+ "@babel/generator": "^7.20.5",
+ "@babel/helper-compilation-targets": "^7.20.0",
+ "@babel/helper-module-transforms": "^7.20.2",
+ "@babel/helpers": "^7.20.5",
+ "@babel/parser": "^7.20.5",
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.5",
+ "@babel/types": "^7.20.5",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.1",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+ "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz",
+ "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.5",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "jsesc": "^2.5.1"
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.20.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz",
+ "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.20.0",
+ "@babel/helper-validator-option": "^7.18.6",
+ "browserslist": "^4.21.3",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-environment-visitor": {
+ "version": "7.18.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
+ "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
+ "dev": true
+ },
+ "@babel/helper-function-name": {
+ "version": "7.19.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
+ "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.18.10",
+ "@babel/types": "^7.19.0"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
+ "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
+ "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz",
+ "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-module-imports": "^7.18.6",
+ "@babel/helper-simple-access": "^7.20.2",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.1",
+ "@babel/types": "^7.20.2"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.20.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz",
+ "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.20.2"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
+ "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.18.6"
+ }
+ },
+ "@babel/helper-string-parser": {
+ "version": "7.19.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+ "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+ "dev": true
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.19.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "dev": true
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
+ "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
+ "dev": true
+ },
+ "@babel/helpers": {
+ "version": "7.20.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.6.tgz",
+ "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.18.10",
+ "@babel/traverse": "^7.20.5",
+ "@babel/types": "^7.20.5"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+ "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.18.6",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "@babel/parser": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz",
+ "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.18.10",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz",
+ "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/parser": "^7.18.10",
+ "@babel/types": "^7.18.10"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz",
+ "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.18.6",
+ "@babel/generator": "^7.20.5",
+ "@babel/helper-environment-visitor": "^7.18.9",
+ "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-hoist-variables": "^7.18.6",
+ "@babel/helper-split-export-declaration": "^7.18.6",
+ "@babel/parser": "^7.20.5",
+ "@babel/types": "^7.20.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/types": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz",
+ "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-string-parser": "^7.19.4",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@compodoc/compodoc": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@compodoc/compodoc/-/compodoc-1.0.9.tgz",
+ "integrity": "sha512-/ur68Kzayyn6vXw6y7MxJRX0Gry4DgxgtGGo8x3AH2lQMkJ2/RWXf0CQSgANHPtM05Zs5DqYqcAqfCCNMLpb/Q==",
+ "dev": true,
+ "requires": {
+ "@compodoc/ngd-transformer": "^2.0.0",
+ "chalk": "^2.3.2",
+ "cheerio": "^1.0.0-rc.2",
+ "chokidar": "^2.0.2",
+ "colors": "^1.1.2",
+ "commander": "2.15.0",
+ "fancy-log": "^1.3.2",
+ "findit2": "^2.2.3",
+ "fs-extra": "^5.0.0",
+ "glob": "^7.1.2",
+ "handlebars": "^4.0.11",
+ "html-entities": "^1.2.1",
+ "json5": "^0.5.1",
+ "live-server": "1.1.0",
+ "lodash": "^4.17.5",
+ "lunr": "2.1.6",
+ "marked": "^0.3.17",
+ "os-name": "^2.0.1",
+ "traverse": "^0.6.6",
+ "ts-simple-ast": "6.12.0",
+ "typescript": "2.4.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ }
+ }
+ },
+ "binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ }
+ },
+ "fs-extra": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
+ "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "typescript": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz",
+ "integrity": "sha512-a6qhFjx88CqXM92QX6e5zwbYavxaknEdFhh/ZrBmuHEP+r2ye102uvhCkWdian4u5Ee17W+8fAN7xtdM8KeQ7A==",
+ "dev": true
+ }
+ }
+ },
+ "@compodoc/ngd-core": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@compodoc/ngd-core/-/ngd-core-2.1.0.tgz",
+ "integrity": "sha512-nyBH7J7SJJ2AV6OeZhJ02kRtVB7ALnZJKgShjoL9CNmOFEj8AkdhP9qTBIgjaDrbsW5pF4nx32KQL2fT7RFnqw==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^4.1.1",
+ "fancy-log": "^1.3.3",
+ "typescript": "^4.0.3"
+ }
+ },
+ "@compodoc/ngd-transformer": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@compodoc/ngd-transformer/-/ngd-transformer-2.1.0.tgz",
+ "integrity": "sha512-Jo4VCMzIUtgIAdRmhHhOoRRE01gCjc5CyrUERRx0VgEzkkCm1Wmu/XHSsQP6tSpCYHBjERghqaDqH5DabkR2oQ==",
+ "dev": true,
+ "requires": {
+ "@aduh95/viz.js": "^3.1.0",
+ "@compodoc/ngd-core": "~2.1.0",
+ "dot": "^1.1.3",
+ "fs-extra": "^9.0.1"
+ },
+ "dependencies": {
+ "fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "requires": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ },
+ "jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ }
+ },
+ "universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true
+ }
+ }
+ },
+ "@esbuild/android-arm": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz",
+ "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-loong64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz",
+ "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@gar/promisify": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
+ "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==",
+ "dev": true
+ },
+ "@jridgewell/gen-mapping": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+ "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true
+ },
+ "@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "dev": true
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.17",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+ "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+ "dev": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
+ },
+ "@npmcli/fs": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz",
+ "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==",
+ "dev": true,
+ "requires": {
+ "@gar/promisify": "^1.1.3",
+ "semver": "^7.3.5"
+ }
+ },
+ "@npmcli/git": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.0.3.tgz",
+ "integrity": "sha512-8cXNkDIbnXPVbhXMmQ7/bklCAjtmPaXfI9aEM4iH+xSuEHINLMHhlfESvVwdqmHJRJkR48vNJTSUvoF6GRPSFA==",
+ "dev": true,
+ "requires": {
+ "@npmcli/promise-spawn": "^6.0.0",
+ "lru-cache": "^7.4.4",
+ "mkdirp": "^1.0.4",
+ "npm-pick-manifest": "^8.0.0",
+ "proc-log": "^3.0.0",
+ "promise-inflight": "^1.0.1",
+ "promise-retry": "^2.0.1",
+ "semver": "^7.3.5",
+ "which": "^3.0.0"
+ },
+ "dependencies": {
+ "proc-log": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz",
+ "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==",
+ "dev": true
+ },
+ "which": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz",
+ "integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
+ "@npmcli/installed-package-contents": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.1.tgz",
+ "integrity": "sha512-GIykAFdOVK31Q1/zAtT5MbxqQL2vyl9mvFJv+OGu01zxbhL3p0xc8gJjdNGX1mWmUT43aEKVO2L6V/2j4TOsAA==",
+ "dev": true,
+ "requires": {
+ "npm-bundled": "^3.0.0",
+ "npm-normalize-package-bin": "^3.0.0"
+ }
+ },
+ "@npmcli/move-file": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz",
+ "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "@npmcli/node-gyp": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz",
+ "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==",
+ "dev": true
+ },
+ "@npmcli/promise-spawn": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.1.tgz",
+ "integrity": "sha512-+hcUpxgx0vEpDJI9Cn+lkTdKLoqKBXFCVps5H7FujEU2vLOp6KwqjLlxbnz8Wzgm8oEqW/u5FeNAXSFjLdCD0A==",
+ "dev": true,
+ "requires": {
+ "which": "^3.0.0"
+ },
+ "dependencies": {
+ "which": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz",
+ "integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
+ "@npmcli/run-script": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.0.tgz",
+ "integrity": "sha512-ql+AbRur1TeOdl1FY+RAwGW9fcr4ZwiVKabdvm93mujGREVuVLbdkXRJDrkTXSdCjaxYydr1wlA2v67jxWG5BQ==",
+ "dev": true,
+ "requires": {
+ "@npmcli/node-gyp": "^3.0.0",
+ "@npmcli/promise-spawn": "^6.0.0",
+ "node-gyp": "^9.0.0",
+ "read-package-json-fast": "^3.0.0",
+ "which": "^3.0.0"
+ },
+ "dependencies": {
+ "which": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz",
+ "integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
+ "@rollup/plugin-json": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-5.0.2.tgz",
+ "integrity": "sha512-D1CoOT2wPvadWLhVcmpkDnesTzjhNIQRWLsc3fA49IFOP2Y84cFOOJ+nKGYedvXHKUsPeq07HR4hXpBBr+CHlA==",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^5.0.1"
+ }
+ },
+ "@rollup/plugin-node-resolve": {
+ "version": "15.0.1",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz",
+ "integrity": "sha512-ReY88T7JhJjeRVbfCyNj+NXAG3IIsVMsX9b5/9jC98dRP8/yxlZdz7mHZbHk5zHr24wZZICS5AcXsFZAXYUQEg==",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^5.0.1",
+ "@types/resolve": "1.20.2",
+ "deepmerge": "^4.2.2",
+ "is-builtin-module": "^3.2.0",
+ "is-module": "^1.0.0",
+ "resolve": "^1.22.1"
+ }
+ },
+ "@rollup/pluginutils": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz",
+ "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "@schematics/angular": {
+ "version": "15.0.2",
+ "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-15.0.2.tgz",
+ "integrity": "sha512-h5vTcOkJD0s+BMHRI87fVyKB+A/YqFolR7fhmhETTq2NwRg2oR0HTf7DbjVxPBwhvI5XzUV1MBPb30xXR2UG0w==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "15.0.2",
+ "@angular-devkit/schematics": "15.0.2",
+ "jsonc-parser": "3.2.0"
+ }
+ },
+ "@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "dev": true
+ },
+ "@types/estree": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
+ "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
+ "dev": true
+ },
+ "@types/jasmine": {
+ "version": "3.3.16",
+ "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.3.16.tgz",
+ "integrity": "sha512-Nveep4zKGby8uIvG2AEUyYOwZS8uVeHK9TgbuWYSawUDDdIgfhCKz28QzamTo//Jk7Ztt9PO3f+vzlB6a4GV1Q==",
+ "dev": true
+ },
+ "@types/jasminewd2": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.10.tgz",
+ "integrity": "sha512-J7mDz7ovjwjc+Y9rR9rY53hFWKATcIkrr9DwQWmOas4/pnIPJTXawnzjwpHm3RSxz/e3ZVUvQ7cRbd5UQLo10g==",
+ "dev": true,
+ "requires": {
+ "@types/jasmine": "*"
+ }
+ },
+ "@types/node": {
+ "version": "12.20.55",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
+ "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==",
+ "dev": true
+ },
+ "@types/q": {
+ "version": "0.0.32",
+ "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
+ "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==",
+ "dev": true
+ },
+ "@types/resolve": {
+ "version": "1.20.2",
+ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
+ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
+ "dev": true
+ },
+ "@types/selenium-webdriver": {
+ "version": "3.0.20",
+ "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.20.tgz",
+ "integrity": "sha512-6d8Q5fqS9DWOXEhMDiF6/2FjyHdmP/jSTAUyeQR7QwrFeNmYyzmvGxD5aLIHL445HjWgibs0eAig+KPnbaesXA==",
+ "dev": true
+ },
+ "@yarnpkg/lockfile": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+ "dev": true
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ }
+ },
+ "adm-zip": {
+ "version": "0.4.16",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
+ "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
+ "dev": true
+ },
+ "after": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+ "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==",
+ "dev": true
+ },
+ "agent-base": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+ "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ },
+ "agentkeepalive": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz",
+ "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "depd": "^1.1.2",
+ "humanize-ms": "^1.2.1"
+ },
+ "dependencies": {
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "dev": true
+ }
+ }
+ },
+ "aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ }
+ },
+ "ajv": {
+ "version": "6.12.4",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
+ "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "dev": true,
+ "requires": {
+ "ajv": "^8.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.11.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz",
+ "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ }
+ }
+ },
+ "ambi": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ambi/-/ambi-2.5.0.tgz",
+ "integrity": "sha512-5nS0gYMPNgZz/UALDHMStcwO42youpIWBQVbI92vV5j0+2bMxv/iVqearrLu3/f0XaU6xVIbf3RRtDxOcHxSkw==",
+ "dev": true,
+ "requires": {
+ "editions": "^1.1.1",
+ "typechecker": "^4.3.0"
+ },
+ "dependencies": {
+ "editions": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz",
+ "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==",
+ "dev": true
+ }
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.21.3"
+ }
+ },
+ "ansi-gray": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
+ "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==",
+ "dev": true,
+ "requires": {
+ "ansi-wrap": "0.1.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "ansi-wrap": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+ "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==",
+ "dev": true
+ },
+ "anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "apache-crypt": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.1.2.tgz",
+ "integrity": "sha512-7wzsZjtf2f7/2yXpjnvRhdI/88/gEG0exlJ9IudSoFJDkJQ/QH7h89K656P0v5DqOJtsdYVVuaN9EElWezlsSQ==",
+ "dev": true,
+ "requires": {
+ "unix-crypt-td-js": "^1.0.0"
+ }
+ },
+ "apache-md5": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.0.6.tgz",
+ "integrity": "sha512-6Nvd712sWdd9jgMZQBYpSPXh/rR/hIwkEtb49lZs0Eva/Ig7pEOWgzRO0HJUU2rqJJv6bYRkKH22cuDloDONaA==",
+ "dev": true
+ },
+ "app-root-path": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz",
+ "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==",
+ "dev": true
+ },
+ "append-transform": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz",
+ "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==",
+ "dev": true,
+ "requires": {
+ "default-require-extensions": "^2.0.0"
+ }
+ },
+ "aproba": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+ "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
+ "dev": true
+ },
+ "are-we-there-yet": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
+ "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
+ "dev": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ },
+ "dependencies": {
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true
+ }
+ }
+ },
+ "aria-query": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
+ "integrity": "sha512-majUxHgLehQTeSA+hClx+DY09OVUqG3GtezWkF1krgLGNdlDu9l9V8DaqNMWbq4Eddc8wsyDA0hpDUtnYxQEXw==",
+ "dev": true,
+ "requires": {
+ "ast-types-flow": "0.0.7",
+ "commander": "^2.11.0"
+ }
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
+ "dev": true
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
+ "dev": true
+ },
+ "array-differ": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz",
+ "integrity": "sha512-LeZY+DZDRnvP7eMuQ6LHfCzUGxAAIViUBliK24P3hWXL6y4SortgR6Nim6xrkfSLlmH0+k+9NYNwVC2s53ZrYQ==",
+ "dev": true
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+ "dev": true
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
+ "dev": true
+ },
+ "arraybuffer.slice": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
+ "dev": true
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
+ "dev": true
+ },
+ "asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "dev": true
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
+ "dev": true
+ },
+ "ast-types-flow": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+ "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
+ "dev": true
+ },
+ "async": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "async-each": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+ "dev": true
+ },
+ "async-limiter": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+ "dev": true
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true
+ },
+ "at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
+ "autoprefixer": {
+ "version": "10.4.13",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz",
+ "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.21.4",
+ "caniuse-lite": "^1.0.30001426",
+ "fraction.js": "^4.2.0",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ }
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+ "dev": true
+ },
+ "axobject-query": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
+ "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==",
+ "dev": true,
+ "requires": {
+ "ast-types-flow": "0.0.7"
+ }
+ },
+ "backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "base64-arraybuffer": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+ "integrity": "sha512-437oANT9tP582zZMwSvZGy2nmSeAb8DW2me3y+Uv1Wp2Rulr8Mqlyrv3E7MLxmsiaPSMMDmiDVzgE+e8zlMx9g==",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true
+ },
+ "base64id": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
+ "integrity": "sha512-rz8L+d/xByiB/vLVftPkyY215fqNrmasrcJsYkVcm4TgJNz+YXKrFaFAWibSaHkiKoSgMDCb+lipOIRQNGYesw==",
+ "dev": true
+ },
+ "basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.1.2"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
+ }
+ },
+ "batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "dev": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "better-assert": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+ "integrity": "sha512-bYeph2DFlpK1XmGs6fvlLRUN29QISM3GBuUwSFsMY2XRx4AvC0WNCS57j4c/xGrK2RS24C1w3YoBOsw9fT46tQ==",
+ "dev": true,
+ "requires": {
+ "callsite": "1.0.0"
+ }
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true
+ },
+ "bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "requires": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
+ "blob": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+ "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==",
+ "dev": true
+ },
+ "blocking-proxy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz",
+ "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "body-parser": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "on-finished": "2.4.1",
+ "qs": "6.11.0",
+ "raw-body": "2.5.1",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "qs": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "dev": true,
+ "requires": {
+ "side-channel": "^1.0.4"
+ }
+ }
+ }
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browserslist": {
+ "version": "4.21.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
+ "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001400",
+ "electron-to-chromium": "^1.4.251",
+ "node-releases": "^2.0.6",
+ "update-browserslist-db": "^1.0.9"
+ }
+ },
+ "browserstack": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz",
+ "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==",
+ "dev": true,
+ "requires": {
+ "https-proxy-agent": "^2.2.1"
+ }
+ },
+ "buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "buffer-alloc": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
+ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+ "dev": true,
+ "requires": {
+ "buffer-alloc-unsafe": "^1.1.0",
+ "buffer-fill": "^1.0.0"
+ }
+ },
+ "buffer-alloc-unsafe": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
+ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
+ "dev": true
+ },
+ "buffer-fill": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
+ "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
+ "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
+ "dev": true
+ },
+ "builtins": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
+ "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==",
+ "dev": true,
+ "requires": {
+ "semver": "^7.0.0"
+ }
+ },
+ "bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true
+ },
+ "cacache": {
+ "version": "17.0.2",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.2.tgz",
+ "integrity": "sha512-rYUs2x4OjSgCQND7nTrh21AHIBFgd7s/ctAYvU3a8u+nK+R5YaX/SFPDYz4Azz7SGL6+6L9ZZWI4Kawpb7grzQ==",
+ "dev": true,
+ "requires": {
+ "@npmcli/fs": "^3.1.0",
+ "fs-minipass": "^2.1.0",
+ "glob": "^8.0.1",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "ssri": "^10.0.0",
+ "tar": "^6.1.11",
+ "unique-filename": "^3.0.0"
+ },
+ "dependencies": {
+ "@npmcli/fs": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz",
+ "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==",
+ "dev": true,
+ "requires": {
+ "semver": "^7.3.5"
+ }
+ },
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
+ "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ }
+ },
+ "minimatch": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz",
+ "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "unique-filename": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz",
+ "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==",
+ "dev": true,
+ "requires": {
+ "unique-slug": "^4.0.0"
+ }
+ },
+ "unique-slug": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz",
+ "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==",
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4"
+ }
+ }
+ }
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ }
+ },
+ "callsite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+ "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001436",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz",
+ "integrity": "sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg==",
+ "dev": true
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "cheerio": {
+ "version": "1.0.0-rc.12",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
+ "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
+ "dev": true,
+ "requires": {
+ "cheerio-select": "^2.1.0",
+ "dom-serializer": "^2.0.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1",
+ "htmlparser2": "^8.0.1",
+ "parse5": "^7.0.0",
+ "parse5-htmlparser2-tree-adapter": "^7.0.0"
+ }
+ },
+ "cheerio-select": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
+ "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-select": "^5.1.0",
+ "css-what": "^6.1.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.0.1"
+ }
+ },
+ "chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
+ },
+ "chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "cli-spinners": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz",
+ "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==",
+ "dev": true
+ },
+ "cli-width": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+ "dev": true
+ },
+ "code-block-writer": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-6.14.0.tgz",
+ "integrity": "sha512-2pAehvRrEUCHRt4cGuHAyVRadS/vQ37VO+LvJcn4xeUUXAh8/5dpq4VYpsbOhu2h4bEgdCTCZ3RgIMdlq0B6Cg==",
+ "dev": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
+ "dev": true
+ },
+ "codelyzer": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-5.2.2.tgz",
+ "integrity": "sha512-jB4FZ1Sx7kZhvZVdf+N2BaKTdrrNZOL0Bj10RRfrhHrb3zEvXjJvvq298JPMJAiyiCS/v4zs1QlGU0ip7xGqeA==",
+ "dev": true,
+ "requires": {
+ "app-root-path": "^2.2.1",
+ "aria-query": "^3.0.0",
+ "axobject-query": "2.0.2",
+ "css-selector-tokenizer": "^0.7.1",
+ "cssauron": "^1.4.0",
+ "damerau-levenshtein": "^1.0.4",
+ "semver-dsl": "^1.0.1",
+ "source-map": "^0.5.7",
+ "sprintf-js": "^1.1.2"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "dev": true
+ }
+ }
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.15.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.0.tgz",
+ "integrity": "sha512-7B1ilBwtYSbetCgTY1NJFg+gVpestg0fdA1MhC1Vs4ssyfSXnCAjFr+QcQM9/RedXC0EaUx1sG8Smgw2VfgKEg==",
+ "dev": true
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true
+ },
+ "compare-versions": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz",
+ "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==",
+ "dev": true
+ },
+ "component-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+ "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "component-inherit": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+ "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "connect": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.4.1.tgz",
+ "integrity": "sha512-lRub47ccjmmdQoA1d+rwRcWsHoKsRyKtZ3z/IMg7/xMS5sWBBuOdAqoKm1xEsxTSWLcBjj8zdcbM6dwwOhgQZA==",
+ "dev": true,
+ "requires": {
+ "debug": "~2.2.0",
+ "finalhandler": "0.4.1",
+ "parseurl": "~1.3.1",
+ "utils-merge": "1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha512-X0rGvJcskG1c3TgSCPqHJ0XJgwlcvOC7elJ5Y0hYuKBZoVqWpAMfLOeIh2UI/DCQ5ruodIjvsugZtjUYUw2pUw==",
+ "dev": true,
+ "requires": {
+ "ms": "0.7.1"
+ }
+ },
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha512-lRLiIR9fSNpnP6TC4v8+4OU7oStC01esuNowdQ34L+Gk8e5Puoc88IqJ+XAY/B3Mn2ZKis8l8HX90oU8ivzUHg==",
+ "dev": true
+ }
+ }
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==",
+ "dev": true
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "dev": true
+ },
+ "cookie": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+ "integrity": "sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==",
+ "dev": true
+ },
+ "copy-anything": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
+ "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==",
+ "dev": true,
+ "requires": {
+ "is-what": "^3.14.1"
+ }
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
+ "dev": true
+ },
+ "core-js": {
+ "version": "3.26.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz",
+ "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true
+ },
+ "cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4",
+ "vary": "^1"
+ }
+ },
+ "csextends": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/csextends/-/csextends-1.2.0.tgz",
+ "integrity": "sha512-S/8k1bDTJIwuGgQYmsRoE+8P+ohV32WhQ0l4zqrc0XDdxOhjQQD7/wTZwCzoZX53jSX3V/qwjT+OkPTxWQcmjg==",
+ "dev": true
+ },
+ "css-select": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
+ "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ }
+ },
+ "css-selector-tokenizer": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz",
+ "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "fastparse": "^1.1.2"
+ }
+ },
+ "css-what": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+ "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+ "dev": true
+ },
+ "cssauron": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz",
+ "integrity": "sha512-Ht70DcFBh+/ekjVrYS2PlDMdSQEl3OFNmjK6lcn49HptBgilXf/Zwg4uFh9Xn0pX3Q8YOkSjIFOfK2osvdqpBw==",
+ "dev": true,
+ "requires": {
+ "through": "X.X.X"
+ }
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true
+ },
+ "cuint": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
+ "integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==",
+ "dev": true
+ },
+ "custom-event": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+ "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
+ "dev": true
+ },
+ "damerau-levenshtein": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+ "dev": true
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "date-format": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
+ "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true
+ },
+ "decode-uri-component": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
+ "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
+ "dev": true
+ },
+ "deepmerge": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+ "dev": true
+ },
+ "default-require-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz",
+ "integrity": "sha512-B0n2zDIXpzLzKeoEozorDSa1cHc1t0NjmxP0zuAxbizNU2MBqYJJKYXrrFdKuQliojXynrxgd7l4ahfg/+aA5g==",
+ "dev": true,
+ "requires": {
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dev": true,
+ "requires": {
+ "clone": "^1.0.2"
+ }
+ },
+ "define-lazy-prop": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+ "dev": true
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "del": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha512-Z4fzpbIRjOu7lO5jCETSWoqUDVe0IPOlfugBsF6suen2LKDlVb4QZpKEM9P+buNJ4KI1eN7I083w/pbKUpsrWQ==",
+ "dev": true,
+ "requires": {
+ "globby": "^5.0.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "rimraf": "^2.2.8"
+ },
+ "dependencies": {
+ "globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ }
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
+ "dev": true
+ },
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true
+ },
+ "dependency-graph": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
+ "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
+ "dev": true
+ },
+ "destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "dev": true
+ },
+ "di": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
+ "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
+ "dev": true
+ },
+ "dom-serialize": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
+ "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==",
+ "dev": true,
+ "requires": {
+ "custom-event": "~1.0.0",
+ "ent": "~2.2.0",
+ "extend": "^3.0.0",
+ "void-elements": "^2.0.0"
+ }
+ },
+ "dom-serializer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ }
+ },
+ "domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true
+ },
+ "domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.3.0"
+ }
+ },
+ "domutils": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz",
+ "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.1"
+ }
+ },
+ "dot": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dot/-/dot-1.1.3.tgz",
+ "integrity": "sha512-/nt74Rm+PcfnirXGEdhZleTwGC2LMnuKTeeTIlI82xb5loBBoXNYzr2ezCroPSMtilK8EZIfcNZwOcHN+ib1Lg==",
+ "dev": true
+ },
+ "duplexer": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+ "dev": true
+ },
+ "eachr": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/eachr/-/eachr-3.3.0.tgz",
+ "integrity": "sha512-yKWuGwOE283CTgbEuvqXXusLH4VBXnY2nZbDkeWev+cpAXY6zCIADSPLdvfkAROc0t8S4l07U1fateCdEDuuvg==",
+ "dev": true,
+ "requires": {
+ "editions": "^2.2.0",
+ "typechecker": "^4.9.0"
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+ "dev": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "editions": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/editions/-/editions-2.3.1.tgz",
+ "integrity": "sha512-ptGvkwTvGdGfC0hfhKg0MT+TRLRKGtUiWGBInxOm5pz7ssADezahjCUaYuZ8Dr+C05FW0AECIIPt4WBxVINEhA==",
+ "dev": true,
+ "requires": {
+ "errlop": "^2.0.0",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true
+ },
+ "electron-to-chromium": {
+ "version": "1.4.284",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz",
+ "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "dev": true
+ },
+ "encoding": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "iconv-lite": "^0.6.2"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ }
+ }
+ },
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "engine.io": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz",
+ "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "base64id": "1.0.0",
+ "cookie": "0.3.1",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.0",
+ "ws": "~3.3.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "engine.io-client": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
+ "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1",
+ "component-inherit": "0.0.3",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.1",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "ws": "~3.3.1",
+ "xmlhttprequest-ssl": "~1.5.4",
+ "yeast": "0.1.2"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "engine.io-parser": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
+ "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
+ "dev": true,
+ "requires": {
+ "after": "0.8.2",
+ "arraybuffer.slice": "~0.0.7",
+ "base64-arraybuffer": "0.1.5",
+ "blob": "0.0.5",
+ "has-binary2": "~1.0.2"
+ }
+ },
+ "ent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+ "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==",
+ "dev": true
+ },
+ "entities": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
+ "dev": true
+ },
+ "env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true
+ },
+ "err-code": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+ "dev": true
+ },
+ "errlop": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/errlop/-/errlop-2.2.0.tgz",
+ "integrity": "sha512-e64Qj9+4aZzjzzFpZC7p5kmm/ccCrbLhAJplhsDXQFs87XTsXwOpH4s1Io2s90Tau/8r2j9f4l/thhDevRjzxw==",
+ "dev": true
+ },
+ "errno": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+ "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "prr": "~1.0.1"
+ }
+ },
+ "es6-promise": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
+ "dev": true
+ },
+ "es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
+ "dev": true,
+ "requires": {
+ "es6-promise": "^4.0.3"
+ }
+ },
+ "esbuild-android-64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz",
+ "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-android-arm64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz",
+ "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-darwin-64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz",
+ "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-darwin-arm64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz",
+ "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-freebsd-64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz",
+ "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-freebsd-arm64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz",
+ "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-32": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz",
+ "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz",
+ "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-arm": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz",
+ "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-arm64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz",
+ "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-mips64le": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz",
+ "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-ppc64le": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz",
+ "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-riscv64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz",
+ "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-linux-s390x": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz",
+ "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-netbsd-64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz",
+ "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-openbsd-64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz",
+ "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-sunos-64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz",
+ "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-wasm": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.18.tgz",
+ "integrity": "sha512-Bw80Siy8XJi6UIR9f0T5SkMIkiVgvFZgHaOZAQCDcZct6Lj5kBZtpACpDhqfdTUzoDyk7pBn4xEft/T5+0tlXw==",
+ "dev": true
+ },
+ "esbuild-windows-32": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz",
+ "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-windows-64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz",
+ "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==",
+ "dev": true,
+ "optional": true
+ },
+ "esbuild-windows-arm64": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz",
+ "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==",
+ "dev": true,
+ "optional": true
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true
+ },
+ "event-stream": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz",
+ "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==",
+ "dev": true,
+ "requires": {
+ "duplexer": "^0.1.1",
+ "from": "^0.1.7",
+ "map-stream": "0.0.7",
+ "pause-stream": "^0.0.11",
+ "split": "^1.0.1",
+ "stream-combiner": "^0.2.2",
+ "through": "^2.3.8"
+ }
+ },
+ "eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "dev": true
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
+ "dev": true
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "extendr": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/extendr/-/extendr-3.5.0.tgz",
+ "integrity": "sha512-7zpVbnnZy91J4k916ZGwpys56DEgJc/prTXDiqCYe/Mud5pqdVsSc9mG/U6sz3lQEvHs81i8Zi7whsFwifhZyw==",
+ "dev": true,
+ "requires": {
+ "editions": "^2.2.0",
+ "typechecker": "^4.7.0"
+ }
+ },
+ "external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "extract-opts": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/extract-opts/-/extract-opts-3.4.0.tgz",
+ "integrity": "sha512-M7Y+1cJDkzOWqvGH5F/V2qgkD6+uitW3NV9rQGl+pLSVuXZ4IDDQgxxMeLPKcWUyfypBWczIILiroSuhXG7Ytg==",
+ "dev": true,
+ "requires": {
+ "eachr": "^3.2.0",
+ "editions": "^2.2.0",
+ "typechecker": "^4.9.0"
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+ "dev": true
+ },
+ "fancy-log": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
+ "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
+ "dev": true,
+ "requires": {
+ "ansi-gray": "^0.1.1",
+ "color-support": "^1.1.3",
+ "parse-node-version": "^1.0.0",
+ "time-stamp": "^1.0.0"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fastparse": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
+ "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
+ "dev": true
+ },
+ "faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "figures": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "fileset": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz",
+ "integrity": "sha512-UxowFKnAFIwtmSxgKjWAVgjE3Fk7MQJT0ZIyl0NwIFZTrx4913rLaonGJ84V+x/2+w/pe4ULHRns+GZPs1TVuw==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.0.3",
+ "minimatch": "^3.0.3"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "finalhandler": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.1.tgz",
+ "integrity": "sha512-+AkanbaabSCYrDcrU+TcA/8SEyMDAN7mjE6GC71GAlvYDXM4wzUsRqLLS2qPtWecIlkX5+MMZGd2RyxO3yBOfg==",
+ "dev": true,
+ "requires": {
+ "debug": "~2.2.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha512-X0rGvJcskG1c3TgSCPqHJ0XJgwlcvOC7elJ5Y0hYuKBZoVqWpAMfLOeIh2UI/DCQ5ruodIjvsugZtjUYUw2pUw==",
+ "dev": true,
+ "requires": {
+ "ms": "0.7.1"
+ }
+ },
+ "ms": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha512-lRLiIR9fSNpnP6TC4v8+4OU7oStC01esuNowdQ34L+Gk8e5Puoc88IqJ+XAY/B3Mn2ZKis8l8HX90oU8ivzUHg==",
+ "dev": true
+ }
+ }
+ },
+ "find-cache-dir": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
+ "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^3.0.2",
+ "pkg-dir": "^4.1.0"
+ },
+ "dependencies": {
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "findit2": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz",
+ "integrity": "sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog==",
+ "dev": true
+ },
+ "flatted": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+ "dev": true
+ },
+ "follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "dev": true
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
+ "dev": true
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+ "dev": true
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "fraction.js": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
+ "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==",
+ "dev": true
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "dev": true
+ },
+ "from": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
+ "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==",
+ "dev": true
+ },
+ "fs-access": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz",
+ "integrity": "sha512-05cXDIwNbFaoFWaz5gNHlUTbH5whiss/hr/ibzPd4MH3cR4w0ZKeIPiVdbyJurg3O5r/Bjpvn9KOb1/rPMf3nA==",
+ "dev": true,
+ "requires": {
+ "null-check": "^1.0.0"
+ }
+ },
+ "fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "gauge": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
+ "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.0.3 || ^2.0.0",
+ "color-support": "^1.1.3",
+ "console-control-strings": "^1.1.0",
+ "has-unicode": "^2.0.1",
+ "signal-exit": "^3.0.7",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1",
+ "wide-align": "^1.1.5"
+ }
+ },
+ "gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "get-intrinsic": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.3"
+ }
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
+ "dev": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true
+ },
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true
+ },
+ "handlebars": {
+ "version": "4.7.7",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
+ "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.0",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4",
+ "wordwrap": "^1.0.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true
+ }
+ }
+ },
+ "has-binary2": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+ "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+ "dev": true,
+ "requires": {
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==",
+ "dev": true
+ }
+ }
+ },
+ "has-cors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+ "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==",
+ "dev": true
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "hosted-git-info": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz",
+ "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^7.5.1"
+ }
+ },
+ "html-entities": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz",
+ "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==",
+ "dev": true
+ },
+ "html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
+ "htmlparser2": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz",
+ "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "entities": "^4.3.0"
+ }
+ },
+ "http-auth": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-2.4.11.tgz",
+ "integrity": "sha512-WaunN0NDebrCeKKnH1mHoFTJ1nBBufx1UsGj9XU3Na3djUuV8WEBVN6RdDXsSejDJ/Pn8KjfXlGs0ZBElawPrw==",
+ "dev": true,
+ "requires": {
+ "apache-crypt": "1.1.2",
+ "apache-md5": "1.0.6",
+ "node-uuid": "^1.4.7"
+ },
+ "dependencies": {
+ "node-uuid": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
+ "integrity": "sha512-TkCET/3rr9mUuRp+CpO7qfgT++aAxfDRaalQhwPFzI9BY/2rCDn6OfpZOVggi1AXfTPpfkTrg5f5WQx5G1uLxA==",
+ "dev": true
+ }
+ }
+ },
+ "http-cache-semantics": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "requires": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ }
+ },
+ "http-parser-js": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
+ "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==",
+ "dev": true
+ },
+ "http-proxy": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "dev": true,
+ "requires": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "dependencies": {
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "requires": {
+ "debug": "4"
+ }
+ }
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+ "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^4.3.0",
+ "debug": "^3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true
+ },
+ "ignore-walk": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.0.tgz",
+ "integrity": "sha512-bTf9UWe/UP1yxG3QUrj/KOvEhTAUWPcv+WvbFZ28LcqznXabp7Xu6o9y1JEC18+oqODuS7VhTpekV5XvFwsxJg==",
+ "dev": true,
+ "requires": {
+ "minimatch": "^5.0.1"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz",
+ "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ }
+ }
+ },
+ "ignorefs": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/ignorefs/-/ignorefs-1.4.1.tgz",
+ "integrity": "sha512-1whgvOsPWFZRNA/5OFhIk56C9Y39+/CYaRVNvsZZkLymacOSqqdSU53xk8CP3G2u5gz2PX6RLxqKPcsIpDriog==",
+ "dev": true,
+ "requires": {
+ "editions": "^2.2.0",
+ "ignorepatterns": "^1.4.0"
+ }
+ },
+ "ignorepatterns": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ignorepatterns/-/ignorepatterns-1.4.0.tgz",
+ "integrity": "sha512-YPBIFRB25iZD0WiLxmToe80+QU+mZI+bUlEh3Ze/4gbhlXHdQFk0SwAFQtPOiBAoDv3FvhtSTDUCD9DKFsHTRA==",
+ "dev": true,
+ "requires": {
+ "editions": "^2.2.0"
+ }
+ },
+ "image-size": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+ "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==",
+ "dev": true,
+ "optional": true
+ },
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "dev": true
+ },
+ "immutable": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
+ "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
+ "dev": true
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true
+ },
+ "indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==",
+ "dev": true
+ },
+ "infer-owner": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+ "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.6.tgz",
+ "integrity": "sha512-IZUoxEjNjubzrmvzZU4lKP7OnYmX72XRl3sqkfJhBKweKi5rnGi5+IUdlj/H1M+Ip5JQ1WzaDMOBRY90Ajc5jg==",
+ "dev": true
+ },
+ "injection-js": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/injection-js/-/injection-js-2.4.0.tgz",
+ "integrity": "sha512-6jiJt0tCAo9zjHbcwLiPL+IuNe9SQ6a9g0PEzafThW3fOQi0mrmiJGBJvDD6tmhPh8cQHIQtCOrJuBfQME4kPA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==",
+ "dev": true
+ }
+ }
+ },
+ "inquirer": {
+ "version": "8.2.4",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz",
+ "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.1.1",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^3.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.21",
+ "mute-stream": "0.0.8",
+ "ora": "^5.4.1",
+ "run-async": "^2.4.0",
+ "rxjs": "^7.5.5",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "through": "^2.3.6",
+ "wrap-ansi": "^7.0.0"
+ },
+ "dependencies": {
+ "rxjs": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz",
+ "integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==",
+ "dev": true
+ }
+ }
+ },
+ "invert-kv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+ "dev": true
+ },
+ "ip": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
+ "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
+ "dev": true
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-builtin-module": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz",
+ "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==",
+ "dev": true,
+ "requires": {
+ "builtin-modules": "^3.3.0"
+ }
+ },
+ "is-core-module": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+ "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "dev": true
+ },
+ "is-lambda": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz",
+ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==",
+ "dev": true
+ },
+ "is-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+ "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
+ "dev": true
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==",
+ "dev": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "dev": true
+ },
+ "is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true
+ },
+ "is-what": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
+ "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==",
+ "dev": true
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true
+ },
+ "isbinaryfile": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz",
+ "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==",
+ "dev": true,
+ "requires": {
+ "buffer-alloc": "^1.2.0"
+ }
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true
+ },
+ "istanbul-api": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.7.tgz",
+ "integrity": "sha512-LYTOa2UrYFyJ/aSczZi/6lBykVMjCCvUmT64gOe+jPZFy4w6FYfPGqFT2IiQ2BxVHHDOvCD7qrIXb0EOh4uGWw==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.2",
+ "compare-versions": "^3.4.0",
+ "fileset": "^2.0.3",
+ "istanbul-lib-coverage": "^2.0.5",
+ "istanbul-lib-hook": "^2.0.7",
+ "istanbul-lib-instrument": "^3.3.0",
+ "istanbul-lib-report": "^2.0.8",
+ "istanbul-lib-source-maps": "^3.0.6",
+ "istanbul-reports": "^2.2.5",
+ "js-yaml": "^3.13.1",
+ "make-dir": "^2.1.0",
+ "minimatch": "^3.0.4",
+ "once": "^1.4.0"
+ }
+ },
+ "istanbul-lib-coverage": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
+ "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==",
+ "dev": true
+ },
+ "istanbul-lib-hook": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz",
+ "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==",
+ "dev": true,
+ "requires": {
+ "append-transform": "^1.0.0"
+ }
+ },
+ "istanbul-lib-instrument": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz",
+ "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==",
+ "dev": true,
+ "requires": {
+ "@babel/generator": "^7.4.0",
+ "@babel/parser": "^7.4.3",
+ "@babel/template": "^7.4.0",
+ "@babel/traverse": "^7.4.3",
+ "@babel/types": "^7.4.0",
+ "istanbul-lib-coverage": "^2.0.5",
+ "semver": "^6.0.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "istanbul-lib-report": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz",
+ "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==",
+ "dev": true,
+ "requires": {
+ "istanbul-lib-coverage": "^2.0.5",
+ "make-dir": "^2.1.0",
+ "supports-color": "^6.1.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "istanbul-lib-source-maps": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz",
+ "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^2.0.5",
+ "make-dir": "^2.1.0",
+ "rimraf": "^2.6.3",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "istanbul-reports": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz",
+ "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==",
+ "dev": true,
+ "requires": {
+ "html-escaper": "^2.0.0"
+ }
+ },
+ "jasmine": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz",
+ "integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==",
+ "dev": true,
+ "requires": {
+ "exit": "^0.1.2",
+ "glob": "^7.0.6",
+ "jasmine-core": "~2.8.0"
+ },
+ "dependencies": {
+ "jasmine-core": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz",
+ "integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==",
+ "dev": true
+ }
+ }
+ },
+ "jasmine-core": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.4.0.tgz",
+ "integrity": "sha512-HU/YxV4i6GcmiH4duATwAbJQMlE0MsDIR5XmSVxURxKHn3aGAdbY1/ZJFmVRbKtnLwIxxMJD7gYaPsypcbYimg==",
+ "dev": true
+ },
+ "jasmine-spec-reporter": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz",
+ "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==",
+ "dev": true,
+ "requires": {
+ "colors": "1.1.2"
+ },
+ "dependencies": {
+ "colors": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==",
+ "dev": true
+ }
+ }
+ },
+ "jasminewd2": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz",
+ "integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "dev": true
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "json-parse-even-better-errors": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz",
+ "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==",
+ "dev": true
+ },
+ "json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true
+ },
+ "json5": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+ "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==",
+ "dev": true
+ },
+ "jsonc-parser": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
+ "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+ "dev": true
+ },
+ "jsprim": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+ "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ }
+ },
+ "jszip": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+ "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+ "dev": true,
+ "requires": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "karma": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-4.1.0.tgz",
+ "integrity": "sha512-xckiDqyNi512U4dXGOOSyLKPwek6X/vUizSy2f3geYevbLj+UIdvNwbn7IwfUIL2g1GXEPWt/87qFD1fBbl/Uw==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.3.0",
+ "body-parser": "^1.16.1",
+ "braces": "^2.3.2",
+ "chokidar": "^2.0.3",
+ "colors": "^1.1.0",
+ "connect": "^3.6.0",
+ "core-js": "^2.2.0",
+ "di": "^0.0.1",
+ "dom-serialize": "^2.2.0",
+ "flatted": "^2.0.0",
+ "glob": "^7.1.1",
+ "graceful-fs": "^4.1.2",
+ "http-proxy": "^1.13.0",
+ "isbinaryfile": "^3.0.0",
+ "lodash": "^4.17.11",
+ "log4js": "^4.0.0",
+ "mime": "^2.3.1",
+ "minimatch": "^3.0.2",
+ "optimist": "^0.6.1",
+ "qjobs": "^1.1.4",
+ "range-parser": "^1.2.0",
+ "rimraf": "^2.6.0",
+ "safe-buffer": "^5.0.1",
+ "socket.io": "2.1.1",
+ "source-map": "^0.6.1",
+ "tmp": "0.0.33",
+ "useragent": "2.3.0"
+ },
+ "dependencies": {
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ }
+ }
+ },
+ "binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ }
+ },
+ "connect": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
+ "utils-merge": "1.0.1"
+ }
+ },
+ "core-js": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ },
+ "mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "dev": true
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "dev": true
+ }
+ }
+ },
+ "karma-chrome-launcher": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz",
+ "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==",
+ "dev": true,
+ "requires": {
+ "fs-access": "^1.0.0",
+ "which": "^1.2.1"
+ }
+ },
+ "karma-cli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/karma-cli/-/karma-cli-1.0.1.tgz",
+ "integrity": "sha512-A/KNa3Is0F7Vv5SIeq0Wj6yGvAIUkrmSU03Wcles4wIkU5MORUTxzwbYT4Tz0qOofx4upfFQU/uIYfGYtADTaA==",
+ "dev": true,
+ "requires": {
+ "resolve": "^1.1.6"
+ }
+ },
+ "karma-coverage-istanbul-reporter": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.1.1.tgz",
+ "integrity": "sha512-CH8lTi8+kKXGvrhy94+EkEMldLCiUA0xMOiL31vvli9qK0T+qcXJAwWBRVJWnVWxYkTmyWar8lPz63dxX6/z1A==",
+ "dev": true,
+ "requires": {
+ "istanbul-api": "^2.1.6",
+ "minimatch": "^3.0.4"
+ }
+ },
+ "karma-jasmine": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-2.0.1.tgz",
+ "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==",
+ "dev": true,
+ "requires": {
+ "jasmine-core": "^3.3"
+ }
+ },
+ "karma-jasmine-html-reporter": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz",
+ "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ },
+ "lcid": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
+ "dev": true,
+ "requires": {
+ "invert-kv": "^2.0.0"
+ }
+ },
+ "less": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz",
+ "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==",
+ "dev": true,
+ "requires": {
+ "copy-anything": "^2.0.1",
+ "errno": "^0.1.1",
+ "graceful-fs": "^4.1.2",
+ "image-size": "~0.5.0",
+ "make-dir": "^2.1.0",
+ "mime": "^1.4.1",
+ "needle": "^3.1.0",
+ "parse-node-version": "^1.0.1",
+ "source-map": "~0.6.0",
+ "tslib": "^2.3.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "optional": true
+ },
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==",
+ "dev": true
+ }
+ }
+ },
+ "lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dev": true,
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "live-server": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/live-server/-/live-server-1.1.0.tgz",
+ "integrity": "sha512-UQcAx+ju1/hRW6eVl3uEEj6sczJ9zfOS2V42qNX6Bi+7zrrGap+PlSb4KUIBrIfaevG8VfsML9HFVya8FLiWog==",
+ "dev": true,
+ "requires": {
+ "colors": "^1.4.0",
+ "connect": "3.4.x",
+ "cors": "^2.8.5",
+ "event-stream": "^4.0.1",
+ "faye-websocket": "0.11.x",
+ "http-auth": "2.4.x",
+ "morgan": "^1.6.1",
+ "object-assign": "^4.1.1",
+ "opn": "^6.0.0",
+ "proxy-middleware": "^0.15.0",
+ "send": "^0.18.0",
+ "serve-index": "^1.7.2",
+ "watchr": "2.6.x"
+ },
+ "dependencies": {
+ "colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+ "dev": true
+ }
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ }
+ },
+ "log4js": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-4.5.1.tgz",
+ "integrity": "sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw==",
+ "dev": true,
+ "requires": {
+ "date-format": "^2.0.0",
+ "debug": "^4.1.1",
+ "flatted": "^2.0.0",
+ "rfdc": "^1.1.4",
+ "streamroller": "^1.0.6"
+ }
+ },
+ "lru-cache": {
+ "version": "7.14.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz",
+ "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==",
+ "dev": true
+ },
+ "lunr": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.1.6.tgz",
+ "integrity": "sha512-ydJpB8CX8cZ/VE+KMaYaFcZ6+o2LruM6NG76VXdflYTgluvVemz1lW4anE+pyBbLvxJHZdvD1Jy/fOqdzAEJog==",
+ "dev": true
+ },
+ "macos-release": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-1.1.0.tgz",
+ "integrity": "sha512-mmLbumEYMi5nXReB9js3WGsB8UE6cDBWyIO62Z4DNx6GbRhDxHNjA1MlzSpJ2S2KM1wyiPRA0d19uHWYYvMHjA==",
+ "dev": true
+ },
+ "magic-string": {
+ "version": "0.26.7",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz",
+ "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==",
+ "dev": true,
+ "requires": {
+ "sourcemap-codec": "^1.4.8"
+ }
+ },
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "make-fetch-happen": {
+ "version": "10.2.1",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz",
+ "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==",
+ "dev": true,
+ "requires": {
+ "agentkeepalive": "^4.2.1",
+ "cacache": "^16.1.0",
+ "http-cache-semantics": "^4.1.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "is-lambda": "^1.0.1",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-fetch": "^2.0.3",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.3",
+ "promise-retry": "^2.0.1",
+ "socks-proxy-agent": "^7.0.0",
+ "ssri": "^9.0.0"
+ },
+ "dependencies": {
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "requires": {
+ "debug": "4"
+ }
+ },
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "cacache": {
+ "version": "16.1.3",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz",
+ "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==",
+ "dev": true,
+ "requires": {
+ "@npmcli/fs": "^2.1.0",
+ "@npmcli/move-file": "^2.0.0",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.1.0",
+ "glob": "^8.0.1",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "mkdirp": "^1.0.4",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^9.0.0",
+ "tar": "^6.1.11",
+ "unique-filename": "^2.0.0"
+ }
+ },
+ "glob": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
+ "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "minimatch": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz",
+ "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "ssri": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz",
+ "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.1.1"
+ }
+ }
+ }
+ },
+ "map-age-cleaner": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
+ "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
+ "dev": true,
+ "requires": {
+ "p-defer": "^1.0.0"
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
+ "dev": true
+ },
+ "map-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
+ "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==",
+ "dev": true
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "marked": {
+ "version": "0.3.19",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz",
+ "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==",
+ "dev": true
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "dev": true
+ },
+ "mem": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
+ "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
+ "dev": true,
+ "requires": {
+ "map-age-cleaner": "^0.1.1",
+ "mimic-fn": "^2.0.0",
+ "p-is-promise": "^2.0.0"
+ }
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ },
+ "dependencies": {
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ }
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "dev": true
+ },
+ "minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "minipass-collect": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+ "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minipass-fetch": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz",
+ "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==",
+ "dev": true,
+ "requires": {
+ "encoding": "^0.1.13",
+ "minipass": "^3.1.6",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.1.2"
+ }
+ },
+ "minipass-flush": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+ "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minipass-json-stream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz",
+ "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.3.1",
+ "minipass": "^3.0.0"
+ }
+ },
+ "minipass-pipeline": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+ "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minipass-sized": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
+ "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ }
+ },
+ "mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
+ },
+ "moment": {
+ "version": "2.29.4",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
+ },
+ "morgan": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz",
+ "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==",
+ "dev": true,
+ "requires": {
+ "basic-auth": "~2.0.1",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-finished": "~2.3.0",
+ "on-headers": "~1.0.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "multimatch": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz",
+ "integrity": "sha512-0mzK8ymiWdehTBiJh0vClAzGyQbdtyWqzSVx//EK4N/D+599RFlGfTAsKw2zMSABtDG9C6Ul2+t8f2Lbdjf5mA==",
+ "dev": true,
+ "requires": {
+ "array-differ": "^1.0.0",
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "minimatch": "^3.0.0"
+ }
+ },
+ "mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "dev": true
+ },
+ "nanoid": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+ "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+ "dev": true
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "needle": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz",
+ "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "debug": "^3.2.6",
+ "iconv-lite": "^0.6.3",
+ "sax": "^1.2.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ }
+ }
+ },
+ "negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true
+ },
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "ng-packagr": {
+ "version": "15.0.1",
+ "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-15.0.1.tgz",
+ "integrity": "sha512-O0e0mf88XVyWAt69YJivjzC5jJVurwLHHpNxdZNZDvqt05N7SfCJI1HAbRbGBvpWmSYTgAnmdjF3XEpPgAd0EA==",
+ "dev": true,
+ "requires": {
+ "@rollup/plugin-json": "^5.0.0",
+ "@rollup/plugin-node-resolve": "^15.0.0",
+ "ajv": "^8.11.0",
+ "ansi-colors": "^4.1.3",
+ "autoprefixer": "^10.4.12",
+ "browserslist": "^4.21.4",
+ "cacache": "^17.0.0",
+ "chokidar": "^3.5.3",
+ "commander": "^9.4.0",
+ "dependency-graph": "^0.11.0",
+ "esbuild": "^0.15.9",
+ "esbuild-wasm": "^0.15.9",
+ "find-cache-dir": "^3.3.2",
+ "glob": "^8.0.3",
+ "injection-js": "^2.4.0",
+ "jsonc-parser": "^3.2.0",
+ "less": "^4.1.3",
+ "ora": "^5.1.0",
+ "postcss": "^8.4.16",
+ "postcss-url": "^10.1.3",
+ "rollup": "^3.0.0",
+ "rollup-plugin-sourcemaps": "^0.6.3",
+ "rxjs": "^7.5.6",
+ "sass": "^1.55.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.11.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz",
+ "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "commander": {
+ "version": "9.4.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz",
+ "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==",
+ "dev": true
+ },
+ "esbuild": {
+ "version": "0.15.18",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz",
+ "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "@esbuild/android-arm": "0.15.18",
+ "@esbuild/linux-loong64": "0.15.18",
+ "esbuild-android-64": "0.15.18",
+ "esbuild-android-arm64": "0.15.18",
+ "esbuild-darwin-64": "0.15.18",
+ "esbuild-darwin-arm64": "0.15.18",
+ "esbuild-freebsd-64": "0.15.18",
+ "esbuild-freebsd-arm64": "0.15.18",
+ "esbuild-linux-32": "0.15.18",
+ "esbuild-linux-64": "0.15.18",
+ "esbuild-linux-arm": "0.15.18",
+ "esbuild-linux-arm64": "0.15.18",
+ "esbuild-linux-mips64le": "0.15.18",
+ "esbuild-linux-ppc64le": "0.15.18",
+ "esbuild-linux-riscv64": "0.15.18",
+ "esbuild-linux-s390x": "0.15.18",
+ "esbuild-netbsd-64": "0.15.18",
+ "esbuild-openbsd-64": "0.15.18",
+ "esbuild-sunos-64": "0.15.18",
+ "esbuild-windows-32": "0.15.18",
+ "esbuild-windows-64": "0.15.18",
+ "esbuild-windows-arm64": "0.15.18"
+ }
+ },
+ "glob": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
+ "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz",
+ "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "rxjs": {
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.6.0.tgz",
+ "integrity": "sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==",
+ "dev": true
+ }
+ }
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "node-gyp": {
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.3.0.tgz",
+ "integrity": "sha512-A6rJWfXFz7TQNjpldJ915WFb1LnhO4lIve3ANPbWreuEoLoKlFT3sxIepPBkLhM27crW8YmN+pjlgbasH6cH/Q==",
+ "dev": true,
+ "requires": {
+ "env-paths": "^2.2.0",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^10.0.3",
+ "nopt": "^6.0.0",
+ "npmlog": "^6.0.0",
+ "rimraf": "^3.0.2",
+ "semver": "^7.3.5",
+ "tar": "^6.1.2",
+ "which": "^2.0.2"
+ },
+ "dependencies": {
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ }
+ }
+ },
+ "node-releases": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
+ "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==",
+ "dev": true
+ },
+ "nopt": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz",
+ "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==",
+ "dev": true,
+ "requires": {
+ "abbrev": "^1.0.0"
+ }
+ },
+ "normalize-package-data": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz",
+ "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^6.0.0",
+ "is-core-module": "^2.8.1",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ },
+ "dependencies": {
+ "hosted-git-info": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz",
+ "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^7.5.1"
+ }
+ }
+ }
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true
+ },
+ "npm-bundled": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz",
+ "integrity": "sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==",
+ "dev": true,
+ "requires": {
+ "npm-normalize-package-bin": "^3.0.0"
+ }
+ },
+ "npm-install-checks": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.0.0.tgz",
+ "integrity": "sha512-SBU9oFglRVZnfElwAtF14NivyulDqF1VKqqwNsFW9HDcbHMAPHpRSsVFgKuwFGq/hVvWZExz62Th0kvxn/XE7Q==",
+ "dev": true,
+ "requires": {
+ "semver": "^7.1.1"
+ }
+ },
+ "npm-normalize-package-bin": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.0.tgz",
+ "integrity": "sha512-g+DPQSkusnk7HYXr75NtzkIP4+N81i3RPsGFidF3DzHd9MT9wWngmqoeg/fnHFz5MNdtG4w03s+QnhewSLTT2Q==",
+ "dev": true
+ },
+ "npm-package-arg": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.2.tgz",
+ "integrity": "sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^5.0.0",
+ "proc-log": "^2.0.1",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^4.0.0"
+ }
+ },
+ "npm-packlist": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.3.tgz",
+ "integrity": "sha512-a/TxELNw489Y7ZzXe7lRoXhKErNNgguYgBdW8nPXLcPMnMOCANiEjCwC1XDH6/5K6hkjuTrEkwtoT9C/zX3jRg==",
+ "dev": true,
+ "requires": {
+ "ignore-walk": "^6.0.0"
+ }
+ },
+ "npm-pick-manifest": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz",
+ "integrity": "sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA==",
+ "dev": true,
+ "requires": {
+ "npm-install-checks": "^6.0.0",
+ "npm-normalize-package-bin": "^3.0.0",
+ "npm-package-arg": "^10.0.0",
+ "semver": "^7.3.5"
+ },
+ "dependencies": {
+ "hosted-git-info": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz",
+ "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^7.5.1"
+ }
+ },
+ "npm-package-arg": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz",
+ "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^6.0.0",
+ "proc-log": "^3.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^5.0.0"
+ }
+ },
+ "proc-log": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz",
+ "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==",
+ "dev": true
+ },
+ "validate-npm-package-name": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz",
+ "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==",
+ "dev": true,
+ "requires": {
+ "builtins": "^5.0.0"
+ }
+ }
+ }
+ },
+ "npm-registry-fetch": {
+ "version": "14.0.2",
+ "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.2.tgz",
+ "integrity": "sha512-TMenrMagFA9KF81E2bkS5XRyzERK4KXu70vgXt5+i8FcrFeLNgNsc6e5hekTqjDwPDkL3HGn/holWcXDMfnFgw==",
+ "dev": true,
+ "requires": {
+ "make-fetch-happen": "^11.0.0",
+ "minipass": "^3.1.6",
+ "minipass-fetch": "^3.0.0",
+ "minipass-json-stream": "^1.0.1",
+ "minizlib": "^2.1.2",
+ "npm-package-arg": "^10.0.0",
+ "proc-log": "^3.0.0"
+ },
+ "dependencies": {
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "requires": {
+ "debug": "4"
+ }
+ },
+ "hosted-git-info": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz",
+ "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^7.5.1"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "make-fetch-happen": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.0.1.tgz",
+ "integrity": "sha512-clv3IblugXn2CDUmqFhNzii3rjKa46u5wNeivc+QlLXkGI5FjLX3rGboo+y2kwf1pd8W0iDiC384cemeDtw9kw==",
+ "dev": true,
+ "requires": {
+ "agentkeepalive": "^4.2.1",
+ "cacache": "^17.0.0",
+ "http-cache-semantics": "^4.1.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "is-lambda": "^1.0.1",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-fetch": "^3.0.0",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.3",
+ "promise-retry": "^2.0.1",
+ "socks-proxy-agent": "^7.0.0",
+ "ssri": "^10.0.0"
+ }
+ },
+ "minipass-fetch": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.0.tgz",
+ "integrity": "sha512-NSx3k5gR4Q5Ts2poCM/19d45VwhVLBtJZ6ypYcthj2BwmDx/e7lW8Aadnyt3edd2W0ecb+b0o7FYLRYE2AGcQg==",
+ "dev": true,
+ "requires": {
+ "encoding": "^0.1.13",
+ "minipass": "^3.1.6",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.1.2"
+ }
+ },
+ "npm-package-arg": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz",
+ "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^6.0.0",
+ "proc-log": "^3.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^5.0.0"
+ }
+ },
+ "proc-log": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz",
+ "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==",
+ "dev": true
+ },
+ "validate-npm-package-name": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz",
+ "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==",
+ "dev": true,
+ "requires": {
+ "builtins": "^5.0.0"
+ }
+ }
+ }
+ },
+ "npm-run-path": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+ "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
+ "dev": true,
+ "requires": {
+ "path-key": "^2.0.0"
+ }
+ },
+ "npmlog": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
+ "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
+ "dev": true,
+ "requires": {
+ "are-we-there-yet": "^3.0.0",
+ "console-control-strings": "^1.1.0",
+ "gauge": "^4.0.3",
+ "set-blocking": "^2.0.0"
+ }
+ },
+ "nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0"
+ }
+ },
+ "null-check": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz",
+ "integrity": "sha512-j8ZNHg19TyIQOWCGeeQJBuu6xZYIEurf8M1Qsfd8mFrGEfIZytbw18YjKWg+LcO25NowXGZXZpKAx+Ui3TFfDw==",
+ "dev": true
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==",
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true
+ },
+ "object-component": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
+ "integrity": "sha512-S0sN3agnVh2SZNEIGc0N1X4Z5K0JeFbGBrnuZpsxuUh5XLF0BnvWkMjRXo/zGKLd/eghvNIKcx1pQkmUjXIyrA==",
+ "dev": true
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "object-inspect": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+ "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
+ "dev": true
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "open": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
+ "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
+ "dev": true,
+ "requires": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ }
+ },
+ "opn": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz",
+ "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==",
+ "dev": true,
+ "requires": {
+ "is-wsl": "^1.1.0"
+ },
+ "dependencies": {
+ "is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
+ "dev": true
+ }
+ }
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==",
+ "dev": true,
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==",
+ "dev": true
+ },
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==",
+ "dev": true
+ }
+ }
+ },
+ "ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "dev": true,
+ "requires": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ }
+ },
+ "os-locale": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
+ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+ "dev": true,
+ "requires": {
+ "execa": "^1.0.0",
+ "lcid": "^2.0.0",
+ "mem": "^4.0.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "os-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/os-name/-/os-name-2.0.1.tgz",
+ "integrity": "sha512-zzMwE/HWRISM52Q966yHosPJ1alrm5Cpw/C9ZhSid50RsMpLtOwc1rtmWxKn7TggPM+GVLUC3RxgWmklOiUgOQ==",
+ "dev": true,
+ "requires": {
+ "macos-release": "^1.0.0",
+ "win-release": "^1.0.0"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true
+ },
+ "p-defer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
+ "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==",
+ "dev": true
+ },
+ "p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
+ "dev": true
+ },
+ "p-is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
+ "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "pacote": {
+ "version": "15.0.6",
+ "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.0.6.tgz",
+ "integrity": "sha512-dQwcz/sME7QIL+cdrw/jftQfMMXxSo17i2kJ/gnhBhUvvBAsxoBu1lw9B5IzCH/Ce8CvEkG/QYZ6txzKfn0bTw==",
+ "dev": true,
+ "requires": {
+ "@npmcli/git": "^4.0.0",
+ "@npmcli/installed-package-contents": "^2.0.1",
+ "@npmcli/promise-spawn": "^6.0.1",
+ "@npmcli/run-script": "^6.0.0",
+ "cacache": "^17.0.0",
+ "fs-minipass": "^2.1.0",
+ "minipass": "^3.1.6",
+ "npm-package-arg": "^10.0.0",
+ "npm-packlist": "^7.0.0",
+ "npm-pick-manifest": "^8.0.0",
+ "npm-registry-fetch": "^14.0.0",
+ "proc-log": "^3.0.0",
+ "promise-retry": "^2.0.1",
+ "read-package-json": "^6.0.0",
+ "read-package-json-fast": "^3.0.0",
+ "ssri": "^10.0.0",
+ "tar": "^6.1.11"
+ },
+ "dependencies": {
+ "hosted-git-info": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz",
+ "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^7.5.1"
+ }
+ },
+ "npm-package-arg": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz",
+ "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^6.0.0",
+ "proc-log": "^3.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^5.0.0"
+ }
+ },
+ "proc-log": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz",
+ "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==",
+ "dev": true
+ },
+ "validate-npm-package-name": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz",
+ "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==",
+ "dev": true,
+ "requires": {
+ "builtins": "^5.0.0"
+ }
+ }
+ }
+ },
+ "pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true
+ },
+ "parse-node-version": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+ "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
+ "dev": true
+ },
+ "parse5": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
+ "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+ "dev": true,
+ "requires": {
+ "entities": "^4.4.0"
+ }
+ },
+ "parse5-htmlparser2-tree-adapter": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
+ "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
+ "dev": true,
+ "requires": {
+ "domhandler": "^5.0.2",
+ "parse5": "^7.0.0"
+ }
+ },
+ "parseqs": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
+ "integrity": "sha512-B3Nrjw2aL7aI4TDujOzfA4NsEc4u1lVcIRE0xesutH8kjeWF70uk+W5cBlIQx04zUH9NTBvuN36Y9xLRPK6Jjw==",
+ "dev": true,
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseuri": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
+ "integrity": "sha512-ijhdxJu6l5Ru12jF0JvzXVPvsC+VibqeaExlNoMhWN6VQ79PGjkmc7oA4W1lp00sFkNyj0fx6ivPLdV51/UMog==",
+ "dev": true,
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
+ "dev": true
+ },
+ "path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "pause-stream": {
+ "version": "0.0.11",
+ "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
+ "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==",
+ "dev": true,
+ "requires": {
+ "through": "~2.3"
+ }
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
+ "dev": true
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "requires": {
+ "find-up": "^4.0.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ }
+ }
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
+ "dev": true
+ },
+ "postcss": {
+ "version": "8.4.19",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz",
+ "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==",
+ "dev": true,
+ "requires": {
+ "nanoid": "^3.3.4",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ }
+ },
+ "postcss-url": {
+ "version": "10.1.3",
+ "resolved": "https://registry.npmjs.org/postcss-url/-/postcss-url-10.1.3.tgz",
+ "integrity": "sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==",
+ "dev": true,
+ "requires": {
+ "make-dir": "~3.1.0",
+ "mime": "~2.5.2",
+ "minimatch": "~3.0.4",
+ "xxhashjs": "~0.2.2"
+ },
+ "dependencies": {
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "mime": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz",
+ "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
+ "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true
+ },
+ "proc-log": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz",
+ "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "promise-inflight": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+ "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+ "dev": true
+ },
+ "promise-retry": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+ "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+ "dev": true,
+ "requires": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ }
+ },
+ "protractor": {
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.4.tgz",
+ "integrity": "sha512-BaL4vePgu3Vfa/whvTUAlgaCAId4uNSGxIFSCXMgj7LMYENPWLp85h5RBi9pdpX/bWQ8SF6flP7afmi2TC4eHw==",
+ "dev": true,
+ "requires": {
+ "@types/q": "^0.0.32",
+ "@types/selenium-webdriver": "^3.0.0",
+ "blocking-proxy": "^1.0.0",
+ "browserstack": "^1.5.1",
+ "chalk": "^1.1.3",
+ "glob": "^7.0.3",
+ "jasmine": "2.8.0",
+ "jasminewd2": "^2.1.0",
+ "q": "1.4.1",
+ "saucelabs": "^1.5.0",
+ "selenium-webdriver": "3.6.0",
+ "source-map-support": "~0.4.0",
+ "webdriver-js-extender": "2.1.0",
+ "webdriver-manager": "^12.0.6",
+ "yargs": "^12.0.5"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "cliui": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
+ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^2.1.1",
+ "strip-ansi": "^4.0.0",
+ "wrap-ansi": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.5.6"
+ }
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true
+ },
+ "webdriver-manager": {
+ "version": "12.1.8",
+ "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.8.tgz",
+ "integrity": "sha512-qJR36SXG2VwKugPcdwhaqcLQOD7r8P2Xiv9sfNbfZrKBnX243iAkOueX1yAmeNgIKhJ3YAT/F2gq6IiEZzahsg==",
+ "dev": true,
+ "requires": {
+ "adm-zip": "^0.4.9",
+ "chalk": "^1.1.1",
+ "del": "^2.2.0",
+ "glob": "^7.0.3",
+ "ini": "^1.3.4",
+ "minimist": "^1.2.0",
+ "q": "^1.4.1",
+ "request": "^2.87.0",
+ "rimraf": "^2.5.2",
+ "semver": "^5.3.0",
+ "xml2js": "^0.4.17"
+ }
+ },
+ "wrap-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1"
+ },
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==",
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ }
+ }
+ },
+ "yargs": {
+ "version": "12.0.5",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
+ "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^4.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^3.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1 || ^4.0.0",
+ "yargs-parser": "^11.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
+ "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "proxy-middleware": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz",
+ "integrity": "sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==",
+ "dev": true
+ },
+ "prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
+ "dev": true,
+ "optional": true
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "q": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
+ "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==",
+ "dev": true
+ },
+ "qjobs": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
+ "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+ "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
+ "dev": true
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ }
+ }
+ },
+ "read-package-json": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.0.tgz",
+ "integrity": "sha512-b/9jxWJ8EwogJPpv99ma+QwtqB7FSl3+V6UXS7Aaay8/5VwMY50oIFooY1UKXMWpfNCM6T/PoGqa5GD1g9xf9w==",
+ "dev": true,
+ "requires": {
+ "glob": "^8.0.1",
+ "json-parse-even-better-errors": "^3.0.0",
+ "normalize-package-data": "^5.0.0",
+ "npm-normalize-package-bin": "^3.0.0"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
+ "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ }
+ },
+ "minimatch": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz",
+ "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ }
+ }
+ },
+ "read-package-json-fast": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.1.tgz",
+ "integrity": "sha512-8+HW7Yo+cjfF+md8DqsZHgats2mxf7gGYow/+2JjxrftoHFZz9v4dzd0EubzYbkNaLxrTVcnllHwklXN2+7aTQ==",
+ "dev": true,
+ "requires": {
+ "json-parse-even-better-errors": "^3.0.0",
+ "npm-normalize-package-bin": "^3.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
+ }
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "reflect-metadata": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
+ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
+ "dev": true
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
+ "dev": true
+ },
+ "repeat-element": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
+ "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
+ "dev": true
+ },
+ "request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ }
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+ "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==",
+ "dev": true
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+ "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "dev": true,
+ "requires": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ }
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
+ "dev": true
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "dev": true
+ },
+ "rfdc": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
+ "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "rollup": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.6.0.tgz",
+ "integrity": "sha512-qCgiBeSu2/AIOKWGFMiRkjPlGlcVwxAjwpGKQZOQYng+83Hip4PjrWHm7EQX1wnrvRqfTytEihRRfLHdX+hR4g==",
+ "dev": true,
+ "requires": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "rollup-plugin-sourcemaps": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz",
+ "integrity": "sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw==",
+ "dev": true,
+ "requires": {
+ "@rollup/pluginutils": "^3.0.9",
+ "source-map-resolve": "^0.6.0"
+ },
+ "dependencies": {
+ "@rollup/pluginutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
+ "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "0.0.39",
+ "estree-walker": "^1.0.1",
+ "picomatch": "^2.2.2"
+ }
+ },
+ "@types/estree": {
+ "version": "0.0.39",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
+ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
+ "dev": true
+ },
+ "estree-walker": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
+ "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
+ "dev": true
+ },
+ "source-map-resolve": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz",
+ "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0"
+ }
+ }
+ }
+ },
+ "run-async": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+ "dev": true
+ },
+ "rxjs": {
+ "version": "6.5.5",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz",
+ "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safefs": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/safefs/-/safefs-4.2.0.tgz",
+ "integrity": "sha512-1amPBO92jw/hWS+gH/u7z7EL7YxaJ8WecBQl49tMQ6Y6EQfndxNNKwlPqDOcwpUetdmK6nKLoVdjybVScRwq5A==",
+ "dev": true,
+ "requires": {
+ "editions": "^2.2.0",
+ "graceful-fs": "^4.2.3"
+ }
+ },
+ "safeps": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/safeps/-/safeps-7.0.1.tgz",
+ "integrity": "sha512-aFREKZzceHZH3KZTwjhDI1oOOcyAEBcQHjImJS/Mmx+KC31EQCgwiPKfwhJLBX7R4Y5ioI2D/VEcQ6U6ya2MJw==",
+ "dev": true,
+ "requires": {
+ "editions": "^1.3.3",
+ "extract-opts": "^3.3.1",
+ "safefs": "^4.1.0",
+ "taskgroup": "^5.0.0",
+ "typechecker": "^4.3.0"
+ },
+ "dependencies": {
+ "editions": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz",
+ "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==",
+ "dev": true
+ }
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "sass": {
+ "version": "1.56.1",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.56.1.tgz",
+ "integrity": "sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==",
+ "dev": true,
+ "requires": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ }
+ },
+ "saucelabs": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz",
+ "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==",
+ "dev": true,
+ "requires": {
+ "https-proxy-agent": "^2.2.1"
+ }
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
+ },
+ "scandirectory": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/scandirectory/-/scandirectory-2.5.0.tgz",
+ "integrity": "sha512-uT0CW8Z3YyoIQs2gXIZgR5miLkN/UNl+5IptQIq1YfD2NhFldikYlC3dkOE6MvF15OZMOxjg8yOjx5J/vIIPUA==",
+ "dev": true,
+ "requires": {
+ "ignorefs": "^1.0.0",
+ "safefs": "^3.1.2",
+ "taskgroup": "^4.0.5"
+ },
+ "dependencies": {
+ "safefs": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/safefs/-/safefs-3.2.2.tgz",
+ "integrity": "sha512-qqvuS8qslGUSgUKQbdsYIK8Qg0EAkykxlsdfy3jpBSnhtyPsee/8y4RLc5+3CD6TgazBmtT0ekoGicUTPzICdg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "*"
+ }
+ },
+ "taskgroup": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/taskgroup/-/taskgroup-4.3.1.tgz",
+ "integrity": "sha512-PD97E2OfwFH7SgeVRvR6K2c+NkKXZSwMMTdcM1t/3P+f70DUWbR81Qx7TF7dJj8dV631u4dhdBmhfDQjIZvGsg==",
+ "dev": true,
+ "requires": {
+ "ambi": "^2.2.0",
+ "csextends": "^1.0.3"
+ }
+ }
+ }
+ },
+ "selenium-webdriver": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
+ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
+ "dev": true,
+ "requires": {
+ "jszip": "^3.1.3",
+ "rimraf": "^2.5.4",
+ "tmp": "0.0.30",
+ "xml2js": "^0.4.17"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "tmp": {
+ "version": "0.0.30",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
+ "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.1"
+ }
+ }
+ }
+ },
+ "semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ }
+ }
+ },
+ "semver-dsl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz",
+ "integrity": "sha512-e8BOaTo007E3dMuQQTnPdalbKTABKNS7UxoBIDnwOqRa+QwMrCPjynB8zAlPF6xlqUfdLPPLIJ13hJNmhtq8Ng==",
+ "dev": true,
+ "requires": {
+ "semver": "^5.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "send": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ }
+ }
+ },
+ "serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "dev": true
+ }
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "dev": true
+ },
+ "set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
+ "dev": true
+ },
+ "side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ }
+ },
+ "signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "dev": true
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "socket.io": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz",
+ "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==",
+ "dev": true,
+ "requires": {
+ "debug": "~3.1.0",
+ "engine.io": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "socket.io-adapter": "~1.1.0",
+ "socket.io-client": "2.1.1",
+ "socket.io-parser": "~3.2.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "socket.io-adapter": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
+ "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==",
+ "dev": true
+ },
+ "socket.io-client": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
+ "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
+ "dev": true,
+ "requires": {
+ "backo2": "1.0.2",
+ "base64-arraybuffer": "0.1.5",
+ "component-bind": "1.0.0",
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "engine.io-client": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "object-component": "0.0.3",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "socket.io-parser": "~3.2.0",
+ "to-array": "0.1.4"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "socket.io-parser": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
+ "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA==",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "socks": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
+ "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
+ "dev": true,
+ "requires": {
+ "ip": "^2.0.0",
+ "smart-buffer": "^4.2.0"
+ }
+ },
+ "socks-proxy-agent": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz",
+ "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^6.0.2",
+ "debug": "^4.3.3",
+ "socks": "^2.6.2"
+ },
+ "dependencies": {
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "requires": {
+ "debug": "4"
+ }
+ },
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+ "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+ "dev": true
+ },
+ "source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "dev": true
+ },
+ "source-map-resolve": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+ "dev": true
+ },
+ "sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz",
+ "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==",
+ "dev": true
+ },
+ "split": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+ "dev": true,
+ "requires": {
+ "through": "2"
+ }
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
+ "dev": true
+ },
+ "sshpk": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+ "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
+ "dev": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ }
+ },
+ "ssri": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.0.tgz",
+ "integrity": "sha512-64ghGOpqW0k+jh7m5jndBGdVEoPikWwGQmBNN5ks6jyUSMymzHDTlnNHOvzp+6MmHOljr2MokUzvRksnTwG0Iw==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.1.1"
+ }
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true
+ },
+ "stream-combiner": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
+ "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==",
+ "dev": true,
+ "requires": {
+ "duplexer": "~0.1.1",
+ "through": "~2.3.4"
+ }
+ },
+ "streamroller": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-1.0.6.tgz",
+ "integrity": "sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.2",
+ "date-format": "^2.0.0",
+ "debug": "^3.2.6",
+ "fs-extra": "^7.0.1",
+ "lodash": "^4.17.14"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ }
+ }
+ },
+ "string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true
+ },
+ "strip-eof": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+ "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true
+ },
+ "symbol-observable": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+ "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+ "dev": true
+ },
+ "tar": {
+ "version": "6.1.12",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz",
+ "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==",
+ "dev": true,
+ "requires": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^3.0.0",
+ "minizlib": "^2.1.1",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ }
+ },
+ "taskgroup": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/taskgroup/-/taskgroup-5.5.0.tgz",
+ "integrity": "sha512-YFkdc6HU+p3xO2lZ1MWdx7R7EbrLF/bpXv5k9635bTzdgOLNbmnsDg5alSpZost+PYMk40d6ZDAJHBHNHiiLvw==",
+ "dev": true,
+ "requires": {
+ "ambi": "3.2.0",
+ "eachr": "^3.2.0",
+ "editions": "^2.2.0",
+ "extendr": "^3.5.0",
+ "safeps": "7.0.1",
+ "unbounded": "^1.2.0"
+ },
+ "dependencies": {
+ "ambi": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ambi/-/ambi-3.2.0.tgz",
+ "integrity": "sha512-nj5sHLPFd7u2OLmHdFs4DHt3gK6edpNw35hTRIKyI/Vd2Th5e4io50rw1lhmCdUNO2Mm4/4FkHmv6shEANAWcw==",
+ "dev": true,
+ "requires": {
+ "editions": "^2.1.0",
+ "typechecker": "^4.3.0"
+ }
+ }
+ }
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+ "dev": true
+ },
+ "time-stamp": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
+ "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==",
+ "dev": true
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "to-array": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+ "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A==",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "dev": true
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true
+ },
+ "tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "traverse": {
+ "version": "0.6.7",
+ "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz",
+ "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==",
+ "dev": true
+ },
+ "ts-simple-ast": {
+ "version": "6.12.0",
+ "resolved": "https://registry.npmjs.org/ts-simple-ast/-/ts-simple-ast-6.12.0.tgz",
+ "integrity": "sha512-0uoRxAxrDLn26DYSHrXLTrj/PiMBnSsxEyeqGfiC1FWCY3LwzXT4mzEhEqIHSizIPfRooPGF5yKByY6y+t48YQ==",
+ "dev": true,
+ "requires": {
+ "code-block-writer": "^6.2.0",
+ "globby": "^6.1.0",
+ "multimatch": "^2.1.0",
+ "object-assign": "^4.1.1",
+ "typescript": "^2.4.x"
+ },
+ "dependencies": {
+ "typescript": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
+ "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==",
+ "dev": true
+ }
+ }
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "dev": true
+ },
+ "type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "typechecker": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/typechecker/-/typechecker-4.11.0.tgz",
+ "integrity": "sha512-lz39Mc/d1UBcF/uQFL5P8L+oWdIn/stvkUgHf0tPRW4aEwGGErewNXo2Nb6We2WslWifn00rhcHbbRWRcTGhuw==",
+ "dev": true,
+ "requires": {
+ "editions": "^2.2.0"
+ }
+ },
+ "typescript": {
+ "version": "4.8.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
+ "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "3.17.4",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
+ "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
+ "dev": true,
+ "optional": true
+ },
+ "ultron": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
+ "dev": true
+ },
+ "unbounded": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/unbounded/-/unbounded-1.3.0.tgz",
+ "integrity": "sha512-RWVCkvcoItljlNTz0iTdBQU6bDj+slVLNaWN7d6DXgH02FfYrz8ytcJ4OPW8b0HqmCehwufJHOIzjHWrQUXBvg==",
+ "dev": true,
+ "requires": {
+ "editions": "^2.2.0"
+ }
+ },
+ "union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ }
+ },
+ "unique-filename": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz",
+ "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==",
+ "dev": true,
+ "requires": {
+ "unique-slug": "^3.0.0"
+ }
+ },
+ "unique-slug": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz",
+ "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==",
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true
+ },
+ "unix-crypt-td-js": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz",
+ "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
+ "dev": true
+ }
+ }
+ },
+ "upath": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "dev": true
+ },
+ "update-browserslist-db": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+ "dev": true,
+ "requires": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ }
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
+ "dev": true
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true
+ },
+ "useragent": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz",
+ "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "4.1.x",
+ "tmp": "0.0.x"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "dev": true,
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
+ "dev": true
+ }
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "utils-merge": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
+ "integrity": "sha512-HwU9SLQEtyo+0uoKXd1nkLqigUWLB+QuNQR4OcmB73eWqksM5ovuqcycks2x043W8XVb75rG1HQ0h93TMXkzQQ==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "validate-npm-package-name": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz",
+ "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==",
+ "dev": true,
+ "requires": {
+ "builtins": "^5.0.0"
+ }
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ },
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+ "dev": true
+ }
+ }
+ },
+ "void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+ "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==",
+ "dev": true
+ },
+ "watchr": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/watchr/-/watchr-2.6.0.tgz",
+ "integrity": "sha512-eHqnPA71jn+lLf/c49mjXqQzzwKLmDdLZXiB53PtgBY8X75zqUWL2PmJWjJ45Bcy8PHOMDdVUCLEud36Lk5QZQ==",
+ "dev": true,
+ "requires": {
+ "eachr": "^3.2.0",
+ "extendr": "^3.2.2",
+ "extract-opts": "^3.3.1",
+ "ignorefs": "^1.1.1",
+ "safefs": "^4.1.0",
+ "scandirectory": "^2.5.0",
+ "taskgroup": "^5.0.1",
+ "typechecker": "^4.3.0"
+ }
+ },
+ "wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dev": true,
+ "requires": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "webdriver-js-extender": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz",
+ "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==",
+ "dev": true,
+ "requires": {
+ "@types/selenium-webdriver": "^3.0.0",
+ "selenium-webdriver": "^3.0.1"
+ }
+ },
+ "websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "dev": true,
+ "requires": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "dev": true
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "dev": true
+ },
+ "wide-align": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
+ "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2 || 3 || 4"
+ }
+ },
+ "win-release": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz",
+ "integrity": "sha512-iCRnKVvGxOQdsKhcQId2PXV1vV3J/sDPXKA4Oe9+Eti2nb2ESEsYHRYls/UjoUW3bIc5ZDO8dTH50A/5iVN+bw==",
+ "dev": true,
+ "requires": {
+ "semver": "^5.0.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "ws": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0",
+ "safe-buffer": "~5.1.0",
+ "ultron": "~1.1.0"
+ },
+ "dependencies": {
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ }
+ }
+ },
+ "xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "dev": true,
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ }
+ },
+ "xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "dev": true
+ },
+ "xmlhttprequest-ssl": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
+ "integrity": "sha512-/bFPLUgJrfGUL10AIv4Y7/CUt6so9CLtB/oFxQSHseSDNNCdC6vwwKEqwLN6wNPBg9YWXAiMu8jkf6RPRS/75Q==",
+ "dev": true
+ },
+ "xxhashjs": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz",
+ "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==",
+ "dev": true,
+ "requires": {
+ "cuint": "^0.2.2"
+ }
+ },
+ "y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "17.6.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz",
+ "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "dependencies": {
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true
+ },
+ "yeast": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+ "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg==",
+ "dev": true
+ },
+ "zone.js": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz",
+ "integrity": "sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^2.3.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
+ "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==",
+ "dev": true
+ }
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index ec2615b..4bdb62d 100644
--- a/package.json
+++ b/package.json
@@ -1,17 +1,56 @@
{
"name": "webglimpse",
- "private": true,
+ "version": "2.6.0",
+ "description": "Webglimpse is a data visualization library for the web.",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/skaughtx0r/webglimpse"
+ },
+ "keywords": [
+ "webglimpse",
+ "data",
+ "visualization",
+ "timeline",
+ "webgl"
+ ],
+ "main": "webglimpse.js",
+ "scripts": {
+ "ng": "ng",
+ "start": "ng serve",
+ "test": "ng test",
+ "lint": "ng lint",
+ "e2e": "ng e2e",
+ "build": "ng-packagr -p ng-package.json --config tsconfig.ng-packagr.json",
+ "docs": "node_modules/.bin/compodoc -p tsconfig.json -d docs --theme readthedocs",
+ "serve-docs": "node_modules/.bin/compodoc -s -d docs"
+ },
+ "dependencies": {
+ "moment": "^2.29.4"
+ },
"devDependencies": {
- "typescript": "1.0.1",
- "grunt": "~0.4.2",
- "grunt-ts": "~1.7.2",
- "grunt-contrib-watch": "~0.5.3",
- "grunt-contrib-connect": "~0.6.0",
- "grunt-contrib-clean": "~0.5.0",
- "grunt-contrib-copy": "~0.5.0",
- "grunt-contrib-uglify": "~0.6.0",
- "grunt-text-replace": "~0.3.10",
- "load-grunt-tasks": "~0.3.0",
- "lodash": "~2.4.1"
+ "@angular/cli": "^15.0.1",
+ "@angular/common": "^15.0.1",
+ "@angular/compiler": "^15.0.1",
+ "@angular/compiler-cli": "^15.0.1",
+ "@angular/core": "^15.0.1",
+ "@compodoc/compodoc": "~1.0.8",
+ "@types/jasmine": "~3.3.13",
+ "@types/jasminewd2": "~2.0.6",
+ "@types/node": "^12.11.1",
+ "codelyzer": "^5.1.0",
+ "core-js": "^3.1.4",
+ "jasmine-core": "~3.4.0",
+ "jasmine-spec-reporter": "~4.2.1",
+ "karma": "~4.1.0",
+ "karma-chrome-launcher": "~2.2.0",
+ "karma-cli": "~1.0.1",
+ "karma-coverage-istanbul-reporter": "^2.0.5",
+ "karma-jasmine": "~2.0.1",
+ "karma-jasmine-html-reporter": "^1.4.2",
+ "ng-packagr": "^15.0.1",
+ "protractor": "~5.4.2",
+ "rxjs": "~6.5.2",
+ "typescript": "~4.8.4",
+ "zone.js": "~0.11.4"
}
}
diff --git a/src/examples/defs/jquery.d.ts b/src/examples/defs/jquery.d.ts
index e0018f6..5e334fa 100644
--- a/src/examples/defs/jquery.d.ts
+++ b/src/examples/defs/jquery.d.ts
@@ -33,7 +33,7 @@ interface JQueryAjaxSettings {
/**
* A pre-request callback function that can be used to modify the jqXHR (in jQuery 1.4.x, XMLHTTPRequest) object before it is sent. Use this to set custom headers, etc. The jqXHR and settings objects are passed as arguments. This is an Ajax Event. Returning false in the beforeSend function will cancel the request. As of jQuery 1.5, the beforeSend option will be called regardless of the type of request.
*/
- beforeSend? (jqXHR: JQueryXHR, settings: JQueryAjaxSettings): any;
+ beforeSend?(jqXHR: JQueryXHR, settings: JQueryAjaxSettings): any;
/**
* If set to false, it will force requested pages not to be cached by the browser. Note: Setting cache to false will only work correctly with HEAD and GET requests. It works by appending "_={timestamp}" to the GET parameters. The parameter is not needed for other types of requests, except in IE8 when a POST is made to a URL that has already been requested by a GET.
*/
@@ -41,7 +41,7 @@ interface JQueryAjaxSettings {
/**
* A function to be called when the request finishes (after success and error callbacks are executed). The function gets passed two arguments: The jqXHR (in jQuery 1.4.x, XMLHTTPRequest) object and a string categorizing the status of the request ("success", "notmodified", "error", "timeout", "abort", or "parsererror"). As of jQuery 1.5, the complete setting can accept an array of functions. Each function will be called in turn. This is an Ajax Event.
*/
- complete? (jqXHR: JQueryXHR, textStatus: string): any;
+ complete?(jqXHR: JQueryXHR, textStatus: string): any;
/**
* An object of string/regular-expression pairs that determine how jQuery will parse the response, given its content type. (version added: 1.5)
*/
@@ -71,7 +71,7 @@ interface JQueryAjaxSettings {
/**
* A function to be used to handle the raw response data of XMLHttpRequest.This is a pre-filtering function to sanitize the response. You should return the sanitized data. The function accepts two arguments: The raw data returned from the server and the 'dataType' parameter.
*/
- dataFilter? (data: any, ty: any): any;
+ dataFilter?(data: any, ty: any): any;
/**
* The type of data that you're expecting back from the server. If none is specified, jQuery will try to infer it based on the MIME type of the response (an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string).
*/
@@ -79,7 +79,7 @@ interface JQueryAjaxSettings {
/**
* A function to be called if the request fails. The function receives three arguments: The jqXHR (in jQuery 1.4.x, XMLHttpRequest) object, a string describing the type of error that occurred and an optional exception object, if one occurred. Possible values for the second argument (besides null) are "timeout", "error", "abort", and "parsererror". When an HTTP error occurs, errorThrown receives the textual portion of the HTTP status, such as "Not Found" or "Internal Server Error." As of jQuery 1.5, the error setting can accept an array of functions. Each function will be called in turn. Note: This handler is not called for cross-domain script and cross-domain JSONP requests. This is an Ajax Event.
*/
- error? (jqXHR: JQueryXHR, textStatus: string, errorThrow: string): any;
+ error?(jqXHR: JQueryXHR, textStatus: string, errorThrow: string): any;
/**
* Whether to trigger global Ajax event handlers for this request. The default is true. Set to false to prevent the global handlers like ajaxStart or ajaxStop from being triggered. This can be used to control various Ajax Events.
*/
@@ -127,7 +127,7 @@ interface JQueryAjaxSettings {
/**
* A function to be called if the request succeeds. The function gets passed three arguments: The data returned from the server, formatted according to the dataType parameter; a string describing the status; and the jqXHR (in jQuery 1.4.x, XMLHttpRequest) object. As of jQuery 1.5, the success setting can accept an array of functions. Each function will be called in turn. This is an Ajax Event.
*/
- success? (data: any, textStatus: string, jqXHR: JQueryXHR): any;
+ success?(data: any, textStatus: string, jqXHR: JQueryXHR): any;
/**
* Set a timeout (in milliseconds) for the request. This will override any global timeout set with $.ajaxSetup(). The timeout period starts at the point the $.ajax call is made; if several other requests are in progress and the browser has no connections available, it is possible for a request to time out before it can be sent. In jQuery 1.4.x and below, the XMLHttpRequest object will be in an invalid state if the request times out; accessing any object members may throw an exception. In Firefox 3.0+ only, script and JSONP requests cannot be cancelled by a timeout; the script will run even if it arrives after the timeout period.
*/
@@ -326,7 +326,7 @@ interface JQuerySupport {
opacity?: boolean;
optDisabled?: boolean;
optSelected?: boolean;
- scriptEval? (): boolean;
+ scriptEval?(): boolean;
style?: boolean;
submitBubbles?: boolean;
tbody?: boolean;
@@ -345,7 +345,7 @@ interface JQueryParam {
*/
interface JQueryEventConstructor {
(name: string, eventProperties?: any): JQueryEventObject;
- new (name: string, eventProperties?: any): JQueryEventObject;
+ new(name: string, eventProperties?: any): JQueryEventObject;
}
/**
@@ -399,11 +399,11 @@ interface JQueryStatic {
ajaxSettings: JQueryAjaxSettings;
- /**
- * Set default values for future Ajax requests. Its use is not recommended.
- *
- * @param options A set of key/value pairs that configure the default Ajax request. All options are optional.
- */
+ /**
+ * Set default values for future Ajax requests. Its use is not recommended.
+ *
+ * @param options A set of key/value pairs that configure the default Ajax request. All options are optional.
+ */
ajaxSetup(options: JQueryAjaxSettings): void;
/**
@@ -628,7 +628,7 @@ interface JQueryStatic {
map(array: any, callback: (elementOfArray: any, indexInArray: any) => any): any;
merge(first: T[], second: T[]): T[];
- merge(first: T[], second: U[]): any[];
+ merge(first: T[], second: U[]): any[];
noop(): any;
@@ -783,7 +783,7 @@ interface JQuery {
* @param attributes An object of attribute-value pairs to set.
*/
attr(attributes: Object): JQuery;
-
+
/**
* Determine whether any of the matched elements are assigned the given class.
*
diff --git a/src/examples/defs/jqueryui.d.ts b/src/examples/defs/jqueryui.d.ts
index ebf7458..541de5a 100644
--- a/src/examples/defs/jqueryui.d.ts
+++ b/src/examples/defs/jqueryui.d.ts
@@ -413,12 +413,12 @@ declare module JQueryUI {
}
interface SelectableEvents {
- selected? (event: Event, ui: { selected?: Element; }): void;
- selecting? (event: Event, ui: { selecting?: Element; }): void;
- start? (event: Event, ui: any): void;
- stop? (event: Event, ui: any): void;
- unselected? (event: Event, ui: { unselected: Element; }): void;
- unselecting? (event: Event, ui: { unselecting: Element; }): void;
+ selected?(event: Event, ui: { selected?: Element; }): void;
+ selecting?(event: Event, ui: { selecting?: Element; }): void;
+ start?(event: Event, ui: any): void;
+ stop?(event: Event, ui: any): void;
+ unselected?(event: Event, ui: { unselected: Element; }): void;
+ unselecting?(event: Event, ui: { unselecting: Element; }): void;
}
interface Selectable extends Widget, SelectableOptions, SelectableEvents {
@@ -836,9 +836,9 @@ interface JQuery {
datepicker(): JQuery;
datepicker(methodName: 'destroy'): void;
- datepicker(methodName: 'dialog', date?: Date, onSelect?: ( date : string, datepicker: any ) => void , pos?: any): void;
- datepicker(methodName: 'dialog', date?: string, onSelect?: ( date : string, datepicker: any ) => void , pos?: any): void;
- datepicker(methodName: 'dialog', date?: string, onSelect?: ( date : string, datepicker: any ) => void , options?: JQueryUI.DatepickerOptions, pos?: any): void;
+ datepicker(methodName: 'dialog', date?: Date, onSelect?: (date: string, datepicker: any) => void, pos?: any): void;
+ datepicker(methodName: 'dialog', date?: string, onSelect?: (date: string, datepicker: any) => void, pos?: any): void;
+ datepicker(methodName: 'dialog', date?: string, onSelect?: (date: string, datepicker: any) => void, options?: JQueryUI.DatepickerOptions, pos?: any): void;
datepicker(methodName: 'getDate'): Date;
datepicker(methodName: 'hide'): void;
datepicker(methodName: 'isDisabled'): boolean;
diff --git a/src/examples/plot/plot.ts b/src/examples/plot/plot.ts
index 972d9e2..04ede74 100644
--- a/src/examples/plot/plot.ts
+++ b/src/examples/plot/plot.ts
@@ -27,92 +27,99 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { newDrawable, Pane } from '../../webglimpse/core';
+import { rgb, black, green, yellow, cyan, magenta } from '../../webglimpse/color';
+import { newAxis2D, attachAxisMouseListeners2D, attachAxisMouseListeners1D } from '../../webglimpse/plot/axis';
+import { newBackgroundPainter, Side } from '../../webglimpse/misc';
+import { newBorderPainter } from '../../webglimpse/painter/border_painter';
+import { newEdgeAxisPainter } from '../../webglimpse/plot/edge_axis_painter';
+import { newPlotLayout } from '../../webglimpse/plot/plot_layout';
+import { jet, reverseBone } from '../../webglimpse/gradient';
- export function runPlotExample( container : Node ) {
+export function runPlotExample(container: Node) {
- // DOM Setup
- //
+ // DOM Setup
+ //
- var canvas = document.createElement( 'canvas' );
- canvas.id = 'exampleCanvas';
- canvas.style.padding = '0';
- container.appendChild( canvas );
+ var canvas = document.createElement('canvas');
+ canvas.id = 'exampleCanvas';
+ canvas.style.padding = '0';
+ container.appendChild(canvas);
- var drawable = newDrawable( canvas );
+ var drawable = newDrawable(canvas);
- var updateCanvasSize = function( ) {
- canvas.width = $( canvas ).width( );
- canvas.height = $( canvas ).height( );
- drawable.redraw( );
- };
- $( window ).resize( updateCanvasSize );
- updateCanvasSize( );
+ var updateCanvasSize = function () {
+ canvas.width = $(canvas).width();
+ canvas.height = $(canvas).height();
+ drawable.redraw();
+ };
+ $(window).resize(updateCanvasSize);
+ updateCanvasSize();
- // Settings
- //
+ // Settings
+ //
- var bgColor = rgb( 0.965, 0.957, 0.949 );
- var textColor = black;
- var tickColor = black;
+ var bgColor = rgb(0.965, 0.957, 0.949);
+ var textColor = black;
+ var tickColor = black;
- var xyAxis = newAxis2D( -1, 7, -1000, 5000 );
- var xAxis = xyAxis.xAxis;
- var yAxis = xyAxis.yAxis;
+ var xyAxis = newAxis2D(-1, 7, -1000, 5000);
+ var xAxis = xyAxis.xAxis;
+ var yAxis = xyAxis.yAxis;
- xyAxis.onLimitsChanged( drawable.redraw );
+ xyAxis.onLimitsChanged(drawable.redraw);
- // Panes & Painters
- //
+ // Panes & Painters
+ //
- var centerPane = new Pane( null );
- centerPane.addPainter( newBackgroundPainter( bgColor ) );
- centerPane.addPainter( newBorderPainter( tickColor ) );
- attachAxisMouseListeners2D( centerPane, xyAxis );
+ var centerPane = new Pane(null);
+ centerPane.addPainter(newBackgroundPainter(bgColor));
+ centerPane.addPainter(newBorderPainter(tickColor));
+ attachAxisMouseListeners2D(centerPane, xyAxis);
- var topPane = new Pane( null );
- topPane.addPainter( newBackgroundPainter( green ) );
- topPane.addPainter( newEdgeAxisPainter( xAxis, Side.TOP, { label: 'Top', textColor: textColor, tickColor: tickColor, tickSize : 12, gradientFill : jet, showBorder: true } ) );
- attachAxisMouseListeners1D( topPane, xAxis, false );
+ var topPane = new Pane(null);
+ topPane.addPainter(newBackgroundPainter(green));
+ topPane.addPainter(newEdgeAxisPainter(xAxis, Side.TOP, { label: 'Top', textColor: textColor, tickColor: tickColor, tickSize: 12, gradientFill: jet, showBorder: true }));
+ attachAxisMouseListeners1D(topPane, xAxis, false);
- var leftPane = new Pane( null );
- leftPane.addPainter( newBackgroundPainter( yellow ) );
- leftPane.addPainter( newEdgeAxisPainter( yAxis, Side.LEFT, { label: 'Y', textColor: textColor, tickColor: tickColor, tickSize : 12, gradientFill : reverseBone, showBorder: true } ) );
- attachAxisMouseListeners1D( leftPane, yAxis, true );
+ var leftPane = new Pane(null);
+ leftPane.addPainter(newBackgroundPainter(yellow));
+ leftPane.addPainter(newEdgeAxisPainter(yAxis, Side.LEFT, { label: 'Y', textColor: textColor, tickColor: tickColor, tickSize: 12, gradientFill: reverseBone, showBorder: true }));
+ attachAxisMouseListeners1D(leftPane, yAxis, true);
- var bottomPane = new Pane( null );
- bottomPane.addPainter( newBackgroundPainter( cyan ) );
- bottomPane.addPainter( newEdgeAxisPainter( xAxis, Side.BOTTOM, { label: 'X', textColor: textColor, tickColor: tickColor, tickSize : 12, gradientFill : reverseBone, showBorder: true } ) );
- attachAxisMouseListeners1D( bottomPane, xAxis, false );
+ var bottomPane = new Pane(null);
+ bottomPane.addPainter(newBackgroundPainter(cyan));
+ bottomPane.addPainter(newEdgeAxisPainter(xAxis, Side.BOTTOM, { label: 'X', textColor: textColor, tickColor: tickColor, tickSize: 12, gradientFill: reverseBone, showBorder: true }));
+ attachAxisMouseListeners1D(bottomPane, xAxis, false);
- var rightPane = new Pane( null );
- rightPane.addPainter( newBackgroundPainter( magenta ) );
- rightPane.addPainter( newEdgeAxisPainter( yAxis, Side.RIGHT, { label: 'Right', textColor: textColor, tickColor: tickColor, tickSize : 12, gradientFill : jet, showBorder: true } ) );
- attachAxisMouseListeners1D( rightPane, yAxis, true );
+ var rightPane = new Pane(null);
+ rightPane.addPainter(newBackgroundPainter(magenta));
+ rightPane.addPainter(newEdgeAxisPainter(yAxis, Side.RIGHT, { label: 'Right', textColor: textColor, tickColor: tickColor, tickSize: 12, gradientFill: jet, showBorder: true }));
+ attachAxisMouseListeners1D(rightPane, yAxis, true);
- var plotPane = new Pane( newPlotLayout( ) );
- plotPane.addPane( topPane, Side.TOP );
- plotPane.addPane( leftPane, Side.LEFT );
- plotPane.addPane( rightPane, Side.RIGHT );
- plotPane.addPane( bottomPane, Side.BOTTOM );
- plotPane.addPane( centerPane, null );
+ var plotPane = new Pane(newPlotLayout());
+ plotPane.addPane(topPane, Side.TOP);
+ plotPane.addPane(leftPane, Side.LEFT);
+ plotPane.addPane(rightPane, Side.RIGHT);
+ plotPane.addPane(bottomPane, Side.BOTTOM);
+ plotPane.addPane(centerPane, null);
- // Show
- //
+ // Show
+ //
- drawable.setContentPane( plotPane );
- drawable.redraw( );
+ drawable.setContentPane(plotPane);
+ drawable.redraw();
- }
+}
+
-}
diff --git a/src/examples/scroll/scroll.ts b/src/examples/scroll/scroll.ts
index 553da56..176631c 100644
--- a/src/examples/scroll/scroll.ts
+++ b/src/examples/scroll/scroll.ts
@@ -1,3 +1,12 @@
+import { createTextTextureFactory } from '../../webglimpse/text';
+import { newDrawable, Pane } from '../../webglimpse/core';
+import { newRowLayout } from '../../webglimpse/layout/row_layout';
+import { newVerticalScrollLayout, newVerticalScrollbar } from '../../webglimpse/scroll';
+import { newBackgroundPainter, fixedSize, newTexturePainter } from '../../webglimpse/misc';
+import { newInsetPane, newInsets } from '../../webglimpse/layout/inset_layout';
+import { white, black, green, blue, cyan } from '../../webglimpse/color';
+import { newColumnLayout } from '../../webglimpse/layout/column_layout';
+
/*
* Copyright (c) 2014, Metron, Inc.
* All rights reserved.
@@ -27,82 +36,82 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
- export function runScrollExample( container : Node ) {
- // DOM Setup
- //
+export function runScrollExample(container: Node) {
- var canvas = document.createElement( 'canvas' );
- canvas.id = 'exampleCanvas';
- canvas.style.padding = '0';
- container.appendChild( canvas );
- var drawable = newDrawable( canvas );
+ // DOM Setup
+ //
- var updateCanvasSize = function( ) {
- canvas.width = $( canvas ).width( );
- canvas.height = $( canvas ).height( );
- drawable.redraw( );
- };
- $( window ).resize( updateCanvasSize );
- updateCanvasSize( );
+ var canvas = document.createElement('canvas');
+ canvas.id = 'exampleCanvas';
+ canvas.style.padding = '0';
+ container.appendChild(canvas);
+ var drawable = newDrawable(canvas);
+ var updateCanvasSize = function () {
+ canvas.width = $(canvas).width();
+ canvas.height = $(canvas).height();
+ drawable.redraw();
+ };
+ $(window).resize(updateCanvasSize);
+ updateCanvasSize();
- // Scroll-pane Setup
- //
- var rowsPane = new Pane( newRowLayout( ) );
- var scrollLayout = newVerticalScrollLayout( );
- var scrollable = new Pane( scrollLayout, false );
- scrollable.addPainter( newBackgroundPainter( white ) );
- scrollable.addPane( newInsetPane( rowsPane, newInsets( 12, 10 ), white ), 0 );
+ // Scroll-pane Setup
+ //
- var scrollbar = newVerticalScrollbar( scrollLayout, drawable );
+ var rowsPane = new Pane(newRowLayout());
- var scrollPane = new Pane( newColumnLayout( false ), false );
- scrollPane.addPane( scrollbar, 0, { width: 16 } );
- scrollPane.addPane( scrollable, 1 );
+ var scrollLayout = newVerticalScrollLayout();
+ var scrollable = new Pane(scrollLayout, false);
+ scrollable.addPainter(newBackgroundPainter(white));
+ scrollable.addPane(newInsetPane(rowsPane, newInsets(12, 10), white), 0);
- drawable.setContentPane( scrollPane );
- drawable.redraw( );
+ var scrollbar = newVerticalScrollbar(scrollLayout, drawable);
+ var scrollPane = new Pane(newColumnLayout(false), false);
+ scrollPane.addPane(scrollbar, 0, { width: 16 });
+ scrollPane.addPane(scrollable, 1);
+ drawable.setContentPane(scrollPane);
+ drawable.redraw();
- // Example Content
- //
- var numRows = 500;
- var rowBgColors = [ black, green, blue, cyan ];
- var rowFgColors = [ white, black, white, black ];
- var createTextTexture = createTextTextureFactory( '20px verdana,sans-serif' );
- for ( var r = 0; r < numRows; r++ ) {
- var row = new Pane( { updatePrefSize: fixedSize( null, 50 ) } );
- row.addPainter( newBackgroundPainter( rowBgColors[ r % rowBgColors.length ] ) );
+ // Example Content
+ //
- var text = r.toFixed( 0 );
- if ( r === 0 ) text += ' first';
- if ( r === numRows-1 ) text += ' last';
+ var numRows = 500;
+ var rowBgColors = [black, green, blue, cyan];
+ var rowFgColors = [white, black, white, black];
- var labelTexture = createTextTexture( rowFgColors[ r % rowFgColors.length ], text );
- row.addPainter( newTexturePainter( labelTexture, 0, 0.5, { xAnchor: 0, yAnchor: labelTexture.yAnchor( 0.5 ) } ) );
+ var createTextTexture = createTextTextureFactory('20px verdana,sans-serif');
+ for (var r = 0; r < numRows; r++) {
+ var row = new Pane({ updatePrefSize: fixedSize(null, 50) });
+ row.addPainter(newBackgroundPainter(rowBgColors[r % rowBgColors.length]));
- setTimeout( ( function( row : Pane, r : number ) {
- return function( ) {
- rowsPane.addPane( row, r );
- drawable.redraw( );
- };
- } )( row, r ), 10*r );
- }
+ var text = r.toFixed(0);
+ if (r === 0) text += ' first';
+ if (r === numRows - 1) text += ' last';
+ var labelTexture = createTextTexture(rowFgColors[r % rowFgColors.length], text);
+ row.addPainter(newTexturePainter(labelTexture, 0, 0.5, { xAnchor: 0, yAnchor: labelTexture.yAnchor(0.5) }));
+ setTimeout((function (row: Pane, r: number) {
+ return function () {
+ rowsPane.addPane(row, r);
+ drawable.redraw();
+ };
+ })(row, r), 10 * r);
}
}
+
+
+
diff --git a/src/examples/timeline/timeline.ts b/src/examples/timeline/timeline.ts
index 8faaa38..ef22b99 100644
--- a/src/examples/timeline/timeline.ts
+++ b/src/examples/timeline/timeline.ts
@@ -27,658 +27,675 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { newDrawable, Pane, PointerEvent, Drawable } from '../../webglimpse/core';
+import { TimeAxis1D } from '../../webglimpse/timeline/time_axis';
+import { TimelinePaneOptions, newTimelinePane, TimelinePane } from '../../webglimpse/timeline/timeline_pane';
+import { white, rgb, gray, rgba } from '../../webglimpse/color';
+import { rowPaneFactoryChooser_THIN } from '../../webglimpse/timeline/timeline_styles';
+import { TimelineModel, TimelineRowModel, TimelineTimeseriesFragmentModel, TimelineEventModel, TimelineGroupModel, Timeline, timelineMergeNewBeforeOld } from '../../webglimpse/timeline/timeline_model';
+import { TimelineUi, TimelineRowUi } from '../../webglimpse/timeline/timeline_ui';
+import { newCornerLayout } from '../../webglimpse/layout/corner_layout';
+import { Side, fixedSize, newBlendingBackgroundPainter } from '../../webglimpse/misc';
+import { newInsetPane, newInsets } from '../../webglimpse/layout/inset_layout';
+import { TimelineEventStyle, TimelineEventStyleUi } from '../../webglimpse/timeline/timeline_event_style';
+import { TimelineAnnotationStyle, TimelineAnnotationStyleUi } from '../../webglimpse/timeline/timeline_annotation_style';
+import { hoursToMillis } from '../../webglimpse/time';
+import { Axis1D, Axis2D, attachAxisMouseListeners1D } from '../../webglimpse/plot/axis';
+import { TimelineRowPaneOptions } from '../../webglimpse/timeline/timeline_row';
+import { reverseBone, jet, getGradientTexture } from '../../webglimpse/gradient';
+import { newColumnLayout } from '../../webglimpse/layout/column_layout';
+import { newHeatmapPainter } from '../../webglimpse/plot/heatmap_painter';
+import { newEdgeAxisPainter } from '../../webglimpse/plot/edge_axis_painter';
+import { isNumber, hasval, parseTime_PMILLIS } from '../../webglimpse/util/util';
+
+
+interface ExampleTooltip {
+ show(html: string, i: number, j: number);
+ move(i: number, j: number);
+ hide();
+}
- interface ExampleTooltip {
- show( html : string, i : number, j : number );
- move( i : number, j : number );
- hide( );
- }
+function newExampleTooltip(): ExampleTooltip {
+ var div = document.createElement('div');
+ div.classList.add('exampleTooltip');
+ div.style.position = 'absolute';
+ div.style.zIndex = '1';
+ div.style.visibility = 'hidden';
+ document.body.appendChild(div);
+
+ return {
+ show: function (html: string, i: number, j: number) {
+ div.innerHTML = html;
+ div.style.left = (i) + 'px';
+ div.style.bottom = (j - div.clientHeight) + 'px';
+ div.style.visibility = 'visible';
+ },
+ move: function (i: number, j: number) {
+ div.style.left = (i) + 'px';
+ div.style.bottom = (j - div.clientHeight) + 'px';
+ },
+ hide: function () {
+ div.style.visibility = 'hidden';
+ }
+ };
+}
- function newExampleTooltip( ) : ExampleTooltip {
- var div = document.createElement( 'div' );
- div.classList.add( 'exampleTooltip' );
- div.style.position = 'absolute';
- div.style.zIndex = '1';
- div.style.visibility = 'hidden';
- document.body.appendChild( div );
-
- return {
- show: function( html : string, i : number, j : number ) {
- div.innerHTML = html;
- div.style.left = ( i ) + 'px';
- div.style.bottom = ( j - div.clientHeight ) + 'px';
- div.style.visibility = 'visible';
- },
- move: function( i : number, j : number ) {
- div.style.left = ( i ) + 'px';
- div.style.bottom = ( j - div.clientHeight ) + 'px';
- },
- hide: function( ) {
- div.style.visibility = 'hidden';
- }
- };
- }
+export function runTimelineExample(container: Node) {
+
+
+ // DOM Setup
+ //
+
+ var canvas = document.createElement('canvas');
+ canvas.id = 'exampleCanvas';
+ canvas.style.padding = '0';
+ container.appendChild(canvas);
+
+ var drawable = newDrawable(canvas);
+
+ var updateCanvasSize = function () {
+ // Make the canvas extend to the bottom of the browser viewport
+ // (can't just set CSS height to 100%, because of the toolbar)
+ $(canvas).height($(window).height() - canvas.getBoundingClientRect().top);
+
+ canvas.width = $(canvas).width();
+ canvas.height = $(canvas).height();
+ drawable.redraw();
+ };
+ $(window).resize(updateCanvasSize);
+ updateCanvasSize();
+
+ // Timeline Setup
+ //
+
+ var timeAxis = new TimeAxis1D(parseTime_PMILLIS('2014-01-01T00:00:00Z'), parseTime_PMILLIS('2014-01-01T12:00:00Z'));
+ timeAxis.limitsChanged.on(drawable.redraw);
+
+ var timelineOptions: TimelinePaneOptions = {
+
+ selectedIntervalMode: 'single',
+ topTimeZone: '-0500',
+ fgColor: white,
+ rowLabelColor: white,
+ groupLabelColor: white,
+ axisLabelColor: white,
+ bgColor: rgb(0.098, 0.165, 0.243),
+ rowBgColor: rgb(0.020, 0.086, 0.165),
+ rowAltBgColor: rgb(0.020, 0.086, 0.165),
+ gridColor: gray(0.5),
+ selectedIntervalFillColor: rgba(0, 0.6, 0.8, 0.157),
+ selectedIntervalBorderColor: rgb(0, 0.2, 1.0),
+ allowEventMultiSelection: true,
+ rowPaneFactoryChooser: rowPaneFactoryChooser_THIN
+
+ };
+ var model = new TimelineModel();
+ var ui = new TimelineUi(model, { allowEventMultiSelection: true });
+ var timelinePane = newTimelinePane(drawable, timeAxis, model, timelineOptions, ui);
+ var selection = ui.selection;
+ selection.selectedInterval.setInterval(parseTime_PMILLIS('2014-01-01T08:30:00Z'), parseTime_PMILLIS('2014-01-01T08:50:00Z'));
+
+ var contentPane = new Pane(newCornerLayout(Side.LEFT, Side.TOP));
+ contentPane.addPane(timelinePane);
+ drawable.setContentPane(newInsetPane(contentPane, newInsets(12, 10, 2), timelineOptions.bgColor));
+ drawable.redraw();
+
+ // Load UI styles
+ //
+
+ $.getJSON('timelineUi.json', function (uiStyles: { eventStyles: TimelineEventStyle[]; annotationStyles: TimelineAnnotationStyle[] }) {
+ uiStyles.eventStyles.forEach(function (s) {
+ ui.eventStyles.add(new TimelineEventStyleUi(s));
+ });
+ uiStyles.annotationStyles.forEach(function (s) {
+ ui.annotationStyles.add(new TimelineAnnotationStyleUi(s));
+ });
+ });
- export function runTimelineExample( container : Node ) {
+ // Example Toolbar Actions
+ //
- // DOM Setup
- //
+ // step in 1 hour increments
+ var timeStep = hoursToMillis(1);
+ var selectedInterval = selection.selectedInterval;
- var canvas = document.createElement( 'canvas' );
- canvas.id = 'exampleCanvas';
- canvas.style.padding = '0';
- container.appendChild( canvas );
+ var a = document.getElementById('selected-time-step-backward');
+ a.onclick = function () {
+ selectedInterval.pan(-timeStep);
+ // if the time selection scrolls off the screen, jump the axis to keep it visible
+ if (selectedInterval.start_PMILLIS < timeAxis.tMin_PMILLIS) {
+ var tSize_MILLIS = timeAxis.tSize_MILLIS;
+ timeAxis.tMin_PMILLIS = selectedInterval.start_PMILLIS;
+ timeAxis.tMax_PMILLIS = timeAxis.tMin_PMILLIS + tSize_MILLIS;
+ }
+ drawable.redraw();
+ };
+
+ var a = document.getElementById('selected-time-step-forward');
+ a.onclick = function () {
+ selectedInterval.pan(timeStep);
+ // if the time selection scrolls off the screen, jump the axis to keep it visible
+ if (selectedInterval.end_PMILLIS > timeAxis.tMax_PMILLIS) {
+ var tSize_MILLIS = timeAxis.tSize_MILLIS;
+ timeAxis.tMax_PMILLIS = selectedInterval.end_PMILLIS;
+ timeAxis.tMin_PMILLIS = timeAxis.tMax_PMILLIS - tSize_MILLIS;
+ }
+ drawable.redraw();
+ };
- var drawable = newDrawable( canvas );
+ var a = document.getElementById('selected-time-decrease');
+ a.onclick = function () {
+ var newDuration_MILLIS = Math.max(selectedInterval.duration_MILLIS - timeStep, timeStep);
+ selectedInterval.start_PMILLIS = selectedInterval.end_PMILLIS - newDuration_MILLIS;
+ drawable.redraw();
+ };
- var updateCanvasSize = function( ) {
- // Make the canvas extend to the bottom of the browser viewport
- // (can't just set CSS height to 100%, because of the toolbar)
- $( canvas ).height( $( window ).height( ) - canvas.getBoundingClientRect( ).top );
+ var a = document.getElementById('selected-time-increase');
+ a.onclick = function () {
+ var newDuration_MILLIS = Math.max(selectedInterval.duration_MILLIS + timeStep, timeStep);
+ selectedInterval.start_PMILLIS = selectedInterval.end_PMILLIS - newDuration_MILLIS;
+ drawable.redraw();
+ };
- canvas.width = $( canvas ).width( );
- canvas.height = $( canvas ).height( );
- drawable.redraw( );
- };
- $( window ).resize( updateCanvasSize );
- updateCanvasSize( );
-
- // Timeline Setup
- //
-
- var timeAxis = new TimeAxis1D( parseTime_PMILLIS( '2014-01-01T00:00:00Z' ), parseTime_PMILLIS( '2014-01-01T12:00:00Z' ) );
- timeAxis.limitsChanged.on( drawable.redraw );
-
- var timelineOptions : TimelinePaneOptions = {
-
- selectedIntervalMode: 'single',
- topTimeZone: '-0500',
- fgColor: white,
- rowLabelColor: white,
- groupLabelColor: white,
- axisLabelColor: white,
- bgColor: rgb( 0.098, 0.165, 0.243 ),
- rowBgColor: rgb( 0.020, 0.086, 0.165 ),
- rowAltBgColor: rgb( 0.020, 0.086, 0.165 ),
- gridColor: gray( 0.5 ),
- selectedIntervalFillColor: rgba( 0, 0.6, 0.8, 0.157 ),
- selectedIntervalBorderColor: rgb( 0, 0.2, 1.0 ),
- allowEventMultiSelection: true,
- rowPaneFactoryChooser: rowPaneFactoryChooser_THIN
+ var a = document.getElementById('selected-time-calendar');
+ a.onclick = function () {
- };
- var model = new TimelineModel( );
- var ui = new TimelineUi( model, { allowEventMultiSelection : true } );
- var timelinePane = newTimelinePane( drawable, timeAxis, model, timelineOptions, ui );
- var selection = ui.selection;
- selection.selectedInterval.setInterval( parseTime_PMILLIS( '2014-01-01T08:30:00Z' ), parseTime_PMILLIS( '2014-01-01T08:50:00Z' ) );
-
- var contentPane = new Pane( newCornerLayout( Side.LEFT, Side.TOP ) );
- contentPane.addPane( timelinePane );
- drawable.setContentPane( newInsetPane( contentPane, newInsets( 12, 10, 2 ), timelineOptions.bgColor ) );
- drawable.redraw( );
-
- // Load UI styles
- //
-
- $.getJSON( 'timelineUi.json', function( uiStyles : { eventStyles : TimelineEventStyle[]; annotationStyles : TimelineAnnotationStyle[] } ) {
- uiStyles.eventStyles.forEach( function( s ) {
- ui.eventStyles.add( new TimelineEventStyleUi( s ) );
- } );
- uiStyles.annotationStyles.forEach( function( s ) {
- ui.annotationStyles.add( new TimelineAnnotationStyleUi( s ) );
- } );
- } );
-
-
-
- // Example Toolbar Actions
- //
-
- // step in 1 hour increments
- var timeStep = hoursToMillis( 1 );
- var selectedInterval = selection.selectedInterval;
-
- var a = document.getElementById( 'selected-time-step-backward' );
- a.onclick = function( ) {
- selectedInterval.pan( -timeStep );
- // if the time selection scrolls off the screen, jump the axis to keep it visible
- if ( selectedInterval.start_PMILLIS < timeAxis.tMin_PMILLIS ) {
- var tSize_MILLIS = timeAxis.tSize_MILLIS;
- timeAxis.tMin_PMILLIS = selectedInterval.start_PMILLIS;
- timeAxis.tMax_PMILLIS = timeAxis.tMin_PMILLIS + tSize_MILLIS;
- }
- drawable.redraw( );
- };
+ var dateFormat = 'M/D/YYYY';
+ var id = 'calendar1';
- var a = document.getElementById( 'selected-time-step-forward' );
- a.onclick = function( ) {
- selectedInterval.pan( timeStep );
- // if the time selection scrolls off the screen, jump the axis to keep it visible
- if ( selectedInterval.end_PMILLIS > timeAxis.tMax_PMILLIS ) {
- var tSize_MILLIS = timeAxis.tSize_MILLIS;
- timeAxis.tMax_PMILLIS = selectedInterval.end_PMILLIS;
- timeAxis.tMin_PMILLIS = timeAxis.tMax_PMILLIS - tSize_MILLIS;
- }
- drawable.redraw( );
- };
+ var calendardiv = document.getElementById(id);
+ if (!calendardiv) {
+ calendardiv = document.createElement('div');
- var a = document.getElementById( 'selected-time-decrease' );
- a.onclick = function( ) {
- var newDuration_MILLIS = Math.max( selectedInterval.duration_MILLIS - timeStep, timeStep );
- selectedInterval.start_PMILLIS = selectedInterval.end_PMILLIS - newDuration_MILLIS;
- drawable.redraw( );
- };
+ calendardiv.style.fontSize = '12';
- var a = document.getElementById( 'selected-time-increase' );
- a.onclick = function( ) {
- var newDuration_MILLIS = Math.max( selectedInterval.duration_MILLIS + timeStep, timeStep );
- selectedInterval.start_PMILLIS = selectedInterval.end_PMILLIS - newDuration_MILLIS;
- drawable.redraw( );
- };
-
- var a = document.getElementById( 'selected-time-calendar' );
- a.onclick = function() {
-
- var dateFormat = 'M/D/YYYY';
- var id = 'calendar1';
-
- var calendardiv = document.getElementById(id);
- if ( !calendardiv ) {
- calendardiv = document.createElement( 'div' );
-
- calendardiv.style.fontSize = '12';
-
- var rect = a.getBoundingClientRect();
- var position: number[] = [rect.left, rect.bottom - 4];
-
- var options = {};
- var callback = function(date, picker) {
- var newTime = moment(date, dateFormat).valueOf();
-
- var selectionDiff = selectedInterval.end_PMILLIS - selectedInterval.start_PMILLIS;
- var axisDiff = timeAxis.tMax_PMILLIS - timeAxis.tMin_PMILLIS;
-
- // jump the selected and axis times
- selectedInterval.start_PMILLIS = newTime - selectionDiff;
- selectedInterval.end_PMILLIS = newTime;
-
- timeAxis.tMin_PMILLIS = newTime - axisDiff;
- timeAxis.tMax_PMILLIS = newTime;
-
- drawable.redraw( );
- }
-
- var date = moment( selectedInterval.start_PMILLIS ).format( dateFormat );
-
- $( calendardiv ).datepicker( 'dialog', date, callback, options, position );
- }
- };
-
- // Link a button to maximize / unmaximize two specific rows
-
- var a = document.getElementById( 'maximize-button' );
- a.onclick = function() {
- if ( model.root.maximizedRowGuids.hasValue( 'metsci.timelineExample.row03a' ) ) {
- model.root.maximizedRowGuids.removeValue( 'metsci.timelineExample.row03a' );
- }
- else {
- model.root.maximizedRowGuids.add( 'metsci.timelineExample.row03a' );
- }
-
- if ( model.root.maximizedRowGuids.hasValue( 'metsci.timelineExample.dynamicRow02' ) ) {
- model.root.maximizedRowGuids.removeValue( 'metsci.timelineExample.dynamicRow02' );
- }
- else {
- model.root.maximizedRowGuids.add( 'metsci.timelineExample.dynamicRow02' );
- }
- };
-
- // Toggle row maximize by double clicking on row label
-
- //add listener for new TimelineRowUi
- ui.rowUis.valueAdded.on( function ( rowUi : TimelineRowUi ) {
- // add listener for new Panes
- rowUi.panes.valueAdded.on( function ( pane : Pane, index : number ) {
- var id = rowUi.panes.idAt( index );
- // test if the new Pane is a maximized row label pane
- if ( id === 'maximized-label' ) {
- // add a mouse listener to the maximized row label Pane
- pane.mouseDown.on( function( event : PointerEvent ) {
- if ( event.clickCount === 2 ) {
- // minimize the double clicked row
- model.root.maximizedRowGuids.removeValue( rowUi.rowGuid );
- }
- } );
- }
- // test if the new Pane is a label pane (id ends with '-label')
- else if ( id.search( '-label$' ) !== -1 ) {
- // add a mouse listener to the row label Pane
- pane.mouseDown.on( function( event : PointerEvent ) {
- if ( event.clickCount === 2 ) {
- // maximize the double clicked row
- model.root.maximizedRowGuids.add( rowUi.rowGuid );
- }
- } );
- }
- } );
- } );
-
- // Link a button to pin / unpin some rows to the top/bottom of the timeline
-
- var a = document.getElementById( 'pin-button' );
- a.onclick = function() {
- if ( model.root.topPinnedRowGuids.hasValue( 'metsci.timelineExample.row01a' ) ) {
- model.root.topPinnedRowGuids.removeValue( 'metsci.timelineExample.row01a' );
- }
- else {
- model.root.topPinnedRowGuids.add( 'metsci.timelineExample.row01a' );
- }
-
- if ( model.root.topPinnedRowGuids.hasValue( 'metsci.timelineExample.row01b' ) ) {
- model.root.topPinnedRowGuids.removeValue( 'metsci.timelineExample.row01b' );
- }
- else {
- model.root.topPinnedRowGuids.add( 'metsci.timelineExample.row01b' );
- }
-
- if ( model.root.bottomPinnedRowGuids.hasValue( 'metsci.timelineExample.row03a' ) ) {
- model.root.bottomPinnedRowGuids.removeValue( 'metsci.timelineExample.row03a' );
- }
- else {
- model.root.bottomPinnedRowGuids.add( 'metsci.timelineExample.row03a' );
+ var rect = a.getBoundingClientRect();
+ var position: number[] = [rect.left, rect.bottom - 4];
+
+ var options = {};
+ var callback = function (date, picker) {
+ var newTime = moment(date, dateFormat).valueOf();
+
+ var selectionDiff = selectedInterval.end_PMILLIS - selectedInterval.start_PMILLIS;
+ var axisDiff = timeAxis.tMax_PMILLIS - timeAxis.tMin_PMILLIS;
+
+ // jump the selected and axis times
+ selectedInterval.start_PMILLIS = newTime - selectionDiff;
+ selectedInterval.end_PMILLIS = newTime;
+
+ timeAxis.tMin_PMILLIS = newTime - axisDiff;
+ timeAxis.tMax_PMILLIS = newTime;
+
+ drawable.redraw();
}
- };
-
- // Link a button change the size of a particular row
-
- var customRowHeight = 135;
- var a = document.getElementById( 'flag-button' );
- a.onclick = function() {
- customRowHeight = customRowHeight + 10;
- if ( customRowHeight > 200 ) customRowHeight = 135;
-
- model.row( 'metsci.timelineExample.row03a' ).rowHeight = customRowHeight;
-
- // make sure no-one references the event
- model.rows.forEach( function( row : TimelineRowModel ) {
- row.eventGuids.removeValue( 'metsci.timelineExample.event01' );
- } );
-
- // remove the event
- model.events.removeId( 'metsci.timelineExample.event01' );
-
- drawable.redraw( );
- };
- // Example Event-Hover Overlay
- //
- // Shows overlay div, containing arbitrary html content, when an event is hovered
- //
-
- var tooltip = newExampleTooltip( );
- var iTooltipOffset = +12;
- var jTooltipOffset = -12;
-
- selection.hoveredEvent.changed.on( function( ) {
- var hoveredEvent = selection.hoveredEvent.value;
- if ( hoveredEvent ) {
- var iMouse = selection.mousePos.x;
- var jMouse = selection.mousePos.y;
- if ( isNumber( iMouse ) && isNumber( jMouse ) ) {
-
- // Generate application-specific html content, based on which event is hovered
- var html = hoveredEvent.label;
-
- tooltip.show( html, iMouse+iTooltipOffset, jMouse+jTooltipOffset );
- }
- else {
- tooltip.hide( );
- }
+ var date = moment(selectedInterval.start_PMILLIS).format(dateFormat);
+
+ $(calendardiv).datepicker('dialog', date, callback, options, position);
+ }
+ };
+
+ // Link a button to maximize / unmaximize two specific rows
+
+ var a = document.getElementById('maximize-button');
+ a.onclick = function () {
+ if (model.root.maximizedRowGuids.hasValue('metsci.timelineExample.row03a')) {
+ model.root.maximizedRowGuids.removeValue('metsci.timelineExample.row03a');
+ }
+ else {
+ model.root.maximizedRowGuids.add('metsci.timelineExample.row03a');
+ }
+
+ if (model.root.maximizedRowGuids.hasValue('metsci.timelineExample.dynamicRow02')) {
+ model.root.maximizedRowGuids.removeValue('metsci.timelineExample.dynamicRow02');
+ }
+ else {
+ model.root.maximizedRowGuids.add('metsci.timelineExample.dynamicRow02');
+ }
+ };
+
+ // Toggle row maximize by double clicking on row label
+
+ //add listener for new TimelineRowUi
+ ui.rowUis.valueAdded.on(function (rowUi: TimelineRowUi) {
+ // add listener for new Panes
+ rowUi.panes.valueAdded.on(function (pane: Pane, index: number) {
+ var id = rowUi.panes.idAt(index);
+ // test if the new Pane is a maximized row label pane
+ if (id === 'maximized-label') {
+ // add a mouse listener to the maximized row label Pane
+ pane.mouseDown.on(function (event: PointerEvent) {
+ if (event.clickCount === 2) {
+ // minimize the double clicked row
+ model.root.maximizedRowGuids.removeValue(rowUi.rowGuid);
+ }
+ });
}
- else {
- tooltip.hide( );
+ // test if the new Pane is a label pane (id ends with '-label')
+ else if (id.search('-label$') !== -1) {
+ // add a mouse listener to the row label Pane
+ pane.mouseDown.on(function (event: PointerEvent) {
+ if (event.clickCount === 2) {
+ // maximize the double clicked row
+ model.root.maximizedRowGuids.add(rowUi.rowGuid);
+ }
+ });
}
- } );
+ });
+ });
+
+ // Link a button to pin / unpin some rows to the top/bottom of the timeline
+
+ var a = document.getElementById('pin-button');
+ a.onclick = function () {
+ if (model.root.topPinnedRowGuids.hasValue('metsci.timelineExample.row01a')) {
+ model.root.topPinnedRowGuids.removeValue('metsci.timelineExample.row01a');
+ }
+ else {
+ model.root.topPinnedRowGuids.add('metsci.timelineExample.row01a');
+ }
+
+ if (model.root.topPinnedRowGuids.hasValue('metsci.timelineExample.row01b')) {
+ model.root.topPinnedRowGuids.removeValue('metsci.timelineExample.row01b');
+ }
+ else {
+ model.root.topPinnedRowGuids.add('metsci.timelineExample.row01b');
+ }
+
+ if (model.root.bottomPinnedRowGuids.hasValue('metsci.timelineExample.row03a')) {
+ model.root.bottomPinnedRowGuids.removeValue('metsci.timelineExample.row03a');
+ }
+ else {
+ model.root.bottomPinnedRowGuids.add('metsci.timelineExample.row03a');
+ }
+ };
+
+ // Link a button change the size of a particular row
+
+ var customRowHeight = 135;
+ var a = document.getElementById('flag-button');
+ a.onclick = function () {
+ customRowHeight = customRowHeight + 10;
+ if (customRowHeight > 200) customRowHeight = 135;
+
+ model.row('metsci.timelineExample.row03a').rowHeight = customRowHeight;
+
+ // make sure no-one references the event
+ model.rows.forEach(function (row: TimelineRowModel) {
+ row.eventGuids.removeValue('metsci.timelineExample.event01');
+ });
+
+ // remove the event
+ model.events.removeId('metsci.timelineExample.event01');
+
+ drawable.redraw();
+ };
+
+ // Example Event-Hover Overlay
+ //
+ // Shows overlay div, containing arbitrary html content, when an event is hovered
+ //
+
+ var tooltip = newExampleTooltip();
+ var iTooltipOffset = +12;
+ var jTooltipOffset = -12;
- selection.mousePos.changed.on( function( ) {
+ selection.hoveredEvent.changed.on(function () {
+ var hoveredEvent = selection.hoveredEvent.value;
+ if (hoveredEvent) {
var iMouse = selection.mousePos.x;
var jMouse = selection.mousePos.y;
- if ( isNumber( iMouse ) && isNumber( jMouse ) ) {
- tooltip.move( iMouse+iTooltipOffset, jMouse+jTooltipOffset );
+ if (isNumber(iMouse) && isNumber(jMouse)) {
+
+ // Generate application-specific html content, based on which event is hovered
+ var html = hoveredEvent.label;
+
+ tooltip.show(html, iMouse + iTooltipOffset, jMouse + jTooltipOffset);
}
else {
- tooltip.hide( );
- }
- } );
-
- // Example Timeseries-Hover Overlay
- //
- // Shows overlay div, containing arbitrary html content, when a timeseries point is hovered
- //
-
- var hoveredFragment : TimelineTimeseriesFragmentModel;
-
- var updateTimeseriesTooltip = function( ) {
-
- // unattach any old data change listener
- if ( hasval( hoveredFragment ) ) {
- hoveredFragment.dataChanged.off( updateTimeseriesTooltip );
+ tooltip.hide();
}
-
- hoveredFragment = selection.hoveredTimeseries.fragment;
-
- if ( hasval( hoveredFragment ) ) {
-
- // attach listener to newly selected fragment to update toolip when fragment data changes
- hoveredFragment.dataChanged.on( updateTimeseriesTooltip );
-
- var iMouse = selection.mousePos.x;
- var jMouse = selection.mousePos.y;
- if ( isNumber( iMouse ) && isNumber( jMouse ) ) {
-
- // Generate application-specific html content, based on which event is hovered
- var html = '' + selection.hoveredTimeseries.data.toFixed(2);
-
- tooltip.show( html, iMouse+iTooltipOffset, jMouse+jTooltipOffset );
- }
- else {
- tooltip.hide( );
- }
+ }
+ else {
+ tooltip.hide();
+ }
+ });
+
+ selection.mousePos.changed.on(function () {
+ var iMouse = selection.mousePos.x;
+ var jMouse = selection.mousePos.y;
+ if (isNumber(iMouse) && isNumber(jMouse)) {
+ tooltip.move(iMouse + iTooltipOffset, jMouse + jTooltipOffset);
+ }
+ else {
+ tooltip.hide();
+ }
+ });
+
+ // Example Timeseries-Hover Overlay
+ //
+ // Shows overlay div, containing arbitrary html content, when a timeseries point is hovered
+ //
+
+ var hoveredFragment: TimelineTimeseriesFragmentModel;
+
+ var updateTimeseriesTooltip = function () {
+
+ // unattach any old data change listener
+ if (hasval(hoveredFragment)) {
+ hoveredFragment.dataChanged.off(updateTimeseriesTooltip);
+ }
+
+ hoveredFragment = selection.hoveredTimeseries.fragment;
+
+ if (hasval(hoveredFragment)) {
+
+ // attach listener to newly selected fragment to update toolip when fragment data changes
+ hoveredFragment.dataChanged.on(updateTimeseriesTooltip);
+
+ var iMouse = selection.mousePos.x;
+ var jMouse = selection.mousePos.y;
+ if (isNumber(iMouse) && isNumber(jMouse)) {
+
+ // Generate application-specific html content, based on which event is hovered
+ var html = '' + selection.hoveredTimeseries.data.toFixed(2);
+
+ tooltip.show(html, iMouse + iTooltipOffset, jMouse + jTooltipOffset);
}
else {
- tooltip.hide( );
+ tooltip.hide();
}
- };
-
- selection.hoveredTimeseries.changed.on( updateTimeseriesTooltip );
-
-
- // Example Input Listeners
- //
- // Fill these in with application-specific input-handling code
- //
-
- selection.hoveredAnnotation.changed.on( function( ) {
- if ( hasval( selection.hoveredAnnotation.value ) )
- {
- // Do something with the hovered annotation
- }
- });
-
- selection.hoveredTimeseries.changed.on( function( ) {
- if ( hasval( selection.hoveredTimeseries.fragment ) )
- {
- // Do something with the time and data value of the selected timeseries point
- var dataValue = selection.hoveredTimeseries.data;
- var time_PMILLIS = selection.hoveredTimeseries.times_PMILLIS;
- }
- } );
-
- selection.mousePos.changed.on( function( ) {
- // Handle mouse position
- var iMouse = selection.mousePos.x; // null if the mouse is outside the timeline
- var jMouse = selection.mousePos.y; // null if the mouse is outside the timeline
- } );
-
- selection.hoveredTime_PMILLIS.changed.on( function( ) {
- // Handle hovered time
- var hoveredTime_PMILLIS = selection.hoveredTime_PMILLIS.value;
- } );
-
- selection.selectedInterval.changed.on( function( ) {
- // Handle selected time interval
- var selectionStart_PMILLIS = selection.selectedInterval.start_PMILLIS;
- var selectionEnd_PMILLIS = selection.selectedInterval.end_PMILLIS;
- } );
-
- selection.hoveredRow.changed.on( function( ) {
- // Handle row hovered
- var hoveredRow = selection.hoveredRow.value;
- } );
-
- selection.hoveredEvent.changed.on( function( ) {
- // Handle event hovered
- var hoveredEvent = selection.hoveredEvent.value;
- } );
-
- selection.selectedEvents.valueAdded.on( function( event : TimelineEventModel ) {
- // Handle event selected
- } );
-
- selection.selectedEvents.valueRemoved.on( function( event : TimelineEventModel ) {
- // Handle event de-selected
- } );
-
-
- // Example Dynamic Data
- //
- // Populate the timeline model with a dynamically generated group, row, and custom painters
- //
-
- var group4 = {
- groupGuid: 'metsci.timelineExample.group04',
- label: 'Group #4',
- rowGuids: []
- };
- model.groups.add( new TimelineGroupModel( group4 ) );
- model.root.groupGuids.add( group4.groupGuid );
+ }
+ else {
+ tooltip.hide();
+ }
+ };
- var row = {
- rowGuid: 'metsci.timelineExample.dynamicRow01',
- label: 'Broadband',
- eventGuids: []
- };
- model.rows.add( new TimelineRowModel( row ) );
- model.group( group4.groupGuid ).rowGuids.add( row.rowGuid );
+ selection.hoveredTimeseries.changed.on(updateTimeseriesTooltip);
- var row = {
- rowGuid: 'metsci.timelineExample.dynamicRow02',
- label: 'Heatmap',
- eventGuids: []
- };
- model.rows.add( new TimelineRowModel( row ) );
- model.group( group4.groupGuid ).rowGuids.add( row.rowGuid );
-
- // create an empty timeseries fragment and listen for changes to its data
- // the fragment will be filled later with data loaded from timeline.json
-
- var fragment3 = new TimelineTimeseriesFragmentModel( { fragmentGuid: 'metsci.timelineExample.fragment03' } );
- model.timeseriesFragments.add( fragment3 );
-
- // print the new data value when fragment3 changes
- // also, ensure that fragment1 data values never exceed 20
- fragment3.dataChanged.on( function( startIndex : number, endIndex : number ) {
- for ( var i = startIndex ; i < endIndex ; i++ ) {
- if ( fragment3.data[i] > 20 ) {
- fragment3.setData( i, 20 );
- }
- }
- } );
-
- // Example Custom Row
- //
- // Create a heatmap row with data loaded form a json file
- //
-
- $.getJSON( 'heatmapData.json', function( json : any ) {
-
- // pull heat map data fields out of the returned json object
- var angleMin = json.minY;
- var angleMax = json.maxY;
- var timeMin = timeAxis.vAtTime( parseTime_PMILLIS( json.minX ) );
- var timeMax = timeAxis.vAtTime( parseTime_PMILLIS( json.maxX ) );
- var ySize = json.sizeY;
- var xSize = json.sizeX;
-
- var array = new Float32Array( xSize * ySize );
- for ( var x = 0 ; x < xSize ; x++ ) {
- for ( var y = 0 ; y < ySize ; y++ ) {
- array[ y * xSize + x ] = json.data[y][x];
- }
+
+ // Example Input Listeners
+ //
+ // Fill these in with application-specific input-handling code
+ //
+
+ selection.hoveredAnnotation.changed.on(function () {
+ if (hasval(selection.hoveredAnnotation.value)) {
+ // Do something with the hovered annotation
+ }
+ });
+
+ selection.hoveredTimeseries.changed.on(function () {
+ if (hasval(selection.hoveredTimeseries.fragment)) {
+ // Do something with the time and data value of the selected timeseries point
+ var dataValue = selection.hoveredTimeseries.data;
+ var time_PMILLIS = selection.hoveredTimeseries.times_PMILLIS;
+ }
+ });
+
+ selection.mousePos.changed.on(function () {
+ // Handle mouse position
+ var iMouse = selection.mousePos.x; // null if the mouse is outside the timeline
+ var jMouse = selection.mousePos.y; // null if the mouse is outside the timeline
+ });
+
+ selection.hoveredTime_PMILLIS.changed.on(function () {
+ // Handle hovered time
+ var hoveredTime_PMILLIS = selection.hoveredTime_PMILLIS.value;
+ });
+
+ selection.selectedInterval.changed.on(function () {
+ // Handle selected time interval
+ var selectionStart_PMILLIS = selection.selectedInterval.start_PMILLIS;
+ var selectionEnd_PMILLIS = selection.selectedInterval.end_PMILLIS;
+ });
+
+ selection.hoveredRow.changed.on(function () {
+ // Handle row hovered
+ var hoveredRow = selection.hoveredRow.value;
+ });
+
+ selection.hoveredEvent.changed.on(function () {
+ // Handle event hovered
+ var hoveredEvent = selection.hoveredEvent.value;
+ });
+
+ selection.selectedEvents.valueAdded.on(function (event: TimelineEventModel) {
+ // Handle event selected
+ });
+
+ selection.selectedEvents.valueRemoved.on(function (event: TimelineEventModel) {
+ // Handle event de-selected
+ });
+
+
+ // Example Dynamic Data
+ //
+ // Populate the timeline model with a dynamically generated group, row, and custom painters
+ //
+
+ var group4 = {
+ groupGuid: 'metsci.timelineExample.group04',
+ label: 'Group #4',
+ rowGuids: []
+ };
+ model.groups.add(new TimelineGroupModel(group4));
+ model.root.groupGuids.add(group4.groupGuid);
+
+ var row = {
+ rowGuid: 'metsci.timelineExample.dynamicRow01',
+ label: 'Broadband',
+ eventGuids: []
+ };
+ model.rows.add(new TimelineRowModel(row));
+ model.group(group4.groupGuid).rowGuids.add(row.rowGuid);
+
+ var row = {
+ rowGuid: 'metsci.timelineExample.dynamicRow02',
+ label: 'Heatmap',
+ eventGuids: []
+ };
+ model.rows.add(new TimelineRowModel(row));
+ model.group(group4.groupGuid).rowGuids.add(row.rowGuid);
+
+ // create an empty timeseries fragment and listen for changes to its data
+ // the fragment will be filled later with data loaded from timeline.json
+
+ var fragment3 = new TimelineTimeseriesFragmentModel({ fragmentGuid: 'metsci.timelineExample.fragment03' });
+ model.timeseriesFragments.add(fragment3);
+
+ // print the new data value when fragment3 changes
+ // also, ensure that fragment1 data values never exceed 20
+ fragment3.dataChanged.on(function (startIndex: number, endIndex: number) {
+ for (var i = startIndex; i < endIndex; i++) {
+ if (fragment3.data[i] > 20) {
+ fragment3.setData(i, 20);
}
-
- var createHeatmapPlotPane = function( drawable : Drawable, timeAxis : TimeAxis1D, yAxis : Axis1D, model : TimelineModel, row : TimelineRowModel, ui : TimelineUi, options : TimelineRowPaneOptions ) : Pane {
- var axisColor = options.timelineFgColor;
-
- var height = options.isMaximized ? null : 270;
-
- // setup axes
- var colorAxis = new Axis1D( -0.7, 0.3 );
- colorAxis.limitsChanged.on( drawable.redraw );
-
- yAxis.setVRange( 0, 180 );
- yAxis.limitsChanged.on( drawable.redraw );
-
- var rowAxis = new Axis2D( timeAxis, yAxis );
-
- var spacerPane = new Pane( { updatePrefSize: fixedSize( null, height ) }, false );
-
- var yAxisPane = new Pane( { updatePrefSize: fixedSize( 40, height ) } );
- attachAxisMouseListeners1D( yAxisPane, yAxis, true );
-
- var colorAxisPane = new Pane( { updatePrefSize: fixedSize( 40, height ) } );
- attachAxisMouseListeners1D( colorAxisPane, colorAxis, true );
-
- var colorInsetPane = newInsetPane( colorAxisPane, newInsets( 10, 10, 10, 10 ), null, false );
- colorInsetPane.addPainter( newBlendingBackgroundPainter( rgba( 0, 0, 0, 0.6 ) ) );
-
- // build arguments to newHeatmapPainter( )
- var data = { array : array, xSize : xSize, ySize : ySize, xMin : timeMin, xMax : timeMax, yMin : angleMin, yMax : angleMax };
- var colorGradient = reverseBone;
- var colorScale = getGradientTexture( colorGradient );
-
- // construct Pane and Painters
- var plotPane = new Pane( newColumnLayout( ), false );
- plotPane.addPainter( newHeatmapPainter( rowAxis, colorAxis, data, colorScale, { blend: true } ) );
- plotPane.addPainter( newEdgeAxisPainter( yAxis, Side.RIGHT, { showLabel: false, textColor: axisColor, tickColor: axisColor, tickSpacing: 34, tickSize: 5, font: '9px verdana,sans-serif' } ) );
- colorAxisPane.addPainter( newEdgeAxisPainter( colorAxis, Side.RIGHT, { showLabel: false, textColor: axisColor, tickColor: axisColor, tickSpacing: 34, tickSize: 10, showBorder : true, gradientFill : colorGradient, font: '9px verdana,sans-serif' } ) );
- plotPane.addPane( yAxisPane, 0 );
- plotPane.addPane( spacerPane, 1 );
- plotPane.addPane( colorInsetPane, 2 );
-
- return plotPane;
+ }
+ });
+
+ // Example Custom Row
+ //
+ // Create a heatmap row with data loaded form a json file
+ //
+
+ $.getJSON('heatmapData.json', function (json: any) {
+
+ // pull heat map data fields out of the returned json object
+ var angleMin = json.minY;
+ var angleMax = json.maxY;
+ var timeMin = timeAxis.vAtTime(parseTime_PMILLIS(json.minX));
+ var timeMax = timeAxis.vAtTime(parseTime_PMILLIS(json.maxX));
+ var ySize = json.sizeY;
+ var xSize = json.sizeX;
+
+ var array = new Float32Array(xSize * ySize);
+ for (var x = 0; x < xSize; x++) {
+ for (var y = 0; y < ySize; y++) {
+ array[y * xSize + x] = json.data[y][x];
}
+ }
- // Override content of an existing row
- ui.rowUi( 'metsci.timelineExample.dynamicRow01' ).paneFactory = createHeatmapPlotPane;
- });
-
- // Another Example Custom Row
- //
- // Create a heatmap row with programmatically generated data
- //
-
- var createHeatmapPlotPane = function( drawable : Drawable, timeAxis : TimeAxis1D, yAxis : Axis1D, model : TimelineModel, row : TimelineRowModel, ui : TimelineUi, options : TimelineRowPaneOptions ) : Pane {
+ var createHeatmapPlotPane = function (drawable: Drawable, timeAxis: TimeAxis1D, yAxis: Axis1D, model: TimelineModel, row: TimelineRowModel, ui: TimelineUi, options: TimelineRowPaneOptions): Pane {
var axisColor = options.timelineFgColor;
var height = options.isMaximized ? null : 270;
-
+
// setup axes
- var colorAxis = new Axis1D( 0, 30 );
- colorAxis.limitsChanged.on( drawable.redraw );
-
- yAxis.setVRange( -400, 400 );
- yAxis.limitsChanged.on( drawable.redraw );
-
- var rowAxis = new Axis2D( timeAxis, yAxis );
-
- var spacerPane = new Pane( { updatePrefSize: fixedSize( null, height ) }, false );
-
- var yAxisPane = new Pane( { updatePrefSize: fixedSize( 40, height ) } );
- attachAxisMouseListeners1D( yAxisPane, yAxis, true );
-
- var colorAxisPane = new Pane( { updatePrefSize: fixedSize( 40, height ) } );
- attachAxisMouseListeners1D( colorAxisPane, colorAxis, true );
-
- var colorInsetPane = newInsetPane( colorAxisPane, newInsets( 10, 10, 10, 10 ), null, false );
- colorInsetPane.addPainter( newBlendingBackgroundPainter( rgba( 0, 0, 0, 0.6 ) ) );
-
- // set heat map min/max bounds (in time and y axis value)
-
- var yMin = -1000;
- var yMax = 1000;
- var timeMin = timeAxis.vAtTime( parseTime_PMILLIS( '2013-12-31T21:00:00Z' ) );
- var timeMax = timeAxis.vAtTime( parseTime_PMILLIS( '2014-01-02T06:19:00Z' ) );
-
- // fill heat map data array
- var xSize = 1000;
- var ySize = 300;
- var w = 2 * Math.PI / 60;
- var fr = 0;
- var array = new Float32Array( xSize * ySize );
- for ( var x = 0 ; x < xSize ; x++ ) {
- for ( var y = 0 ; y < ySize ; y++ ) {
- var x0 = 2 * Math.PI * x / xSize;
- var x1 = 2 * Math.PI * y / ySize;
- var value = 30 * Math.abs( Math.sin( x0 + w * fr ) ) * ( Math.sin( x0 - w * fr ) * Math.sin( 8 * x0 + w * fr ) * Math.cos( -8 * x1 - w * fr ) +Math.abs( Math.sin( 24 * x1 ) ) / 3 );
- array[ y * xSize + x ] = value;
- }
- }
-
+ var colorAxis = new Axis1D(-0.7, 0.3);
+ colorAxis.limitsChanged.on(drawable.redraw);
+
+ yAxis.setVRange(0, 180);
+ yAxis.limitsChanged.on(drawable.redraw);
+
+ var rowAxis = new Axis2D(timeAxis, yAxis);
+
+ var spacerPane = new Pane({ updatePrefSize: fixedSize(null, height) }, false);
+
+ var yAxisPane = new Pane({ updatePrefSize: fixedSize(40, height) });
+ attachAxisMouseListeners1D(yAxisPane, yAxis, true);
+
+ var colorAxisPane = new Pane({ updatePrefSize: fixedSize(40, height) });
+ attachAxisMouseListeners1D(colorAxisPane, colorAxis, true);
+
+ var colorInsetPane = newInsetPane(colorAxisPane, newInsets(10, 10, 10, 10), null, false);
+ colorInsetPane.addPainter(newBlendingBackgroundPainter(rgba(0, 0, 0, 0.6)));
+
// build arguments to newHeatmapPainter( )
- var data = { array : array, xSize : xSize, ySize : ySize, xMin : timeMin, xMax : timeMax, yMin : yMin, yMax : yMax };
- var colorGradient = jet;
- var colorScale = getGradientTexture( colorGradient );
-
+ var data = { array: array, xSize: xSize, ySize: ySize, xMin: timeMin, xMax: timeMax, yMin: angleMin, yMax: angleMax };
+ var colorGradient = reverseBone;
+ var colorScale = getGradientTexture(colorGradient);
+
// construct Pane and Painters
- var plotPane = new Pane( newColumnLayout( ), false );
- plotPane.addPainter( newHeatmapPainter( rowAxis, colorAxis, data, colorScale, { blend: true } ) );
- plotPane.addPainter( newEdgeAxisPainter( yAxis, Side.RIGHT, { showLabel: false, textColor: axisColor, tickColor: axisColor, tickSpacing: 34, tickSize: 5, font: '9px verdana,sans-serif' } ) );
- colorAxisPane.addPainter( newEdgeAxisPainter( colorAxis, Side.RIGHT, { showLabel: false, textColor: axisColor, tickColor: axisColor, tickSpacing: 34, tickSize: 10, showBorder : true, gradientFill : colorGradient, font: '9px verdana,sans-serif' } ) );
- plotPane.addPane( yAxisPane, 0 );
- plotPane.addPane( spacerPane, 1 );
- plotPane.addPane( colorInsetPane, 2 );
+ var plotPane = new Pane(newColumnLayout(), false);
+ plotPane.addPainter(newHeatmapPainter(rowAxis, colorAxis, data, colorScale, { blend: true }));
+ plotPane.addPainter(newEdgeAxisPainter(yAxis, Side.RIGHT, { showLabel: false, textColor: axisColor, tickColor: axisColor, tickSpacing: 34, tickSize: 5, font: '9px verdana,sans-serif' }));
+ colorAxisPane.addPainter(newEdgeAxisPainter(colorAxis, Side.RIGHT, { showLabel: false, textColor: axisColor, tickColor: axisColor, tickSpacing: 34, tickSize: 10, showBorder: true, gradientFill: colorGradient, font: '9px verdana,sans-serif' }));
+ plotPane.addPane(yAxisPane, 0);
+ plotPane.addPane(spacerPane, 1);
+ plotPane.addPane(colorInsetPane, 2);
return plotPane;
-
}
// Override content of an existing row
- ui.rowUi( 'metsci.timelineExample.dynamicRow02' ).paneFactory = createHeatmapPlotPane;
-
- // Example JSON Event loading
- //
- // Fetch timeline data from the server, and merge it into the existing timeline model
- //
- $.getJSON( 'timelineData.json', function( newTimeline : Timeline ) {
- model.merge( newTimeline, timelineMergeNewBeforeOld );
-
- // once the timeline has been loaded, add listeners to some of the Panes
- var pane = ui.rowUi( 'metsci.timelineExample.row03a' ).getPane( 'y-axis' );
- var clickListener = function( ev : PointerEvent ) {
- console.log( 'y-axis click: ' + ev.clickCount );
- };
- pane.mouseDown.on( clickListener );
- } );
-
- // Example function for reloading TimelinePane with new TimelinePaneOptions. Takes care of
- // removing the TimelinePane from its parent pane, disposing of its listeners/resources,
- // creating a new TimelinePane, and reattaching it to the parent Pane. The new TimelinePane
- // is returned.
- //
- // In this example, this function would be called as follows:
- //
- // timelinePane = reloadTimeline( contentPane, timelinePane, timelineOptions );
- //
- var reloadTimeline = function( parentPane : Pane, oldTimelinePane : TimelinePane, newOptions : TimelinePaneOptions ) : TimelinePane {
-
- // remove the old TimelinePane from the parent Pane, dispose of it, and create a new TimelinePane
- parentPane.removePane( oldTimelinePane );
- oldTimelinePane.dispose.fire( );
- var reloadedTimelinePane = newTimelinePane( drawable, timeAxis, model, timelineOptions, oldTimelinePane.ui );
- parentPane.addPane( reloadedTimelinePane );
-
- // update the drawable
- drawable.redraw( );
-
- // return the newly created Pane
- return reloadedTimelinePane;
+ ui.rowUi('metsci.timelineExample.dynamicRow01').paneFactory = createHeatmapPlotPane;
+ });
+
+ // Another Example Custom Row
+ //
+ // Create a heatmap row with programmatically generated data
+ //
+
+ var createHeatmapPlotPane = function (drawable: Drawable, timeAxis: TimeAxis1D, yAxis: Axis1D, model: TimelineModel, row: TimelineRowModel, ui: TimelineUi, options: TimelineRowPaneOptions): Pane {
+ var axisColor = options.timelineFgColor;
+
+ var height = options.isMaximized ? null : 270;
+
+ // setup axes
+ var colorAxis = new Axis1D(0, 30);
+ colorAxis.limitsChanged.on(drawable.redraw);
+
+ yAxis.setVRange(-400, 400);
+ yAxis.limitsChanged.on(drawable.redraw);
+
+ var rowAxis = new Axis2D(timeAxis, yAxis);
+
+ var spacerPane = new Pane({ updatePrefSize: fixedSize(null, height) }, false);
+
+ var yAxisPane = new Pane({ updatePrefSize: fixedSize(40, height) });
+ attachAxisMouseListeners1D(yAxisPane, yAxis, true);
+
+ var colorAxisPane = new Pane({ updatePrefSize: fixedSize(40, height) });
+ attachAxisMouseListeners1D(colorAxisPane, colorAxis, true);
+
+ var colorInsetPane = newInsetPane(colorAxisPane, newInsets(10, 10, 10, 10), null, false);
+ colorInsetPane.addPainter(newBlendingBackgroundPainter(rgba(0, 0, 0, 0.6)));
+
+ // set heat map min/max bounds (in time and y axis value)
+
+ var yMin = -1000;
+ var yMax = 1000;
+ var timeMin = timeAxis.vAtTime(parseTime_PMILLIS('2013-12-31T21:00:00Z'));
+ var timeMax = timeAxis.vAtTime(parseTime_PMILLIS('2014-01-02T06:19:00Z'));
+
+ // fill heat map data array
+ var xSize = 1000;
+ var ySize = 300;
+ var w = 2 * Math.PI / 60;
+ var fr = 0;
+ var array = new Float32Array(xSize * ySize);
+ for (var x = 0; x < xSize; x++) {
+ for (var y = 0; y < ySize; y++) {
+ var x0 = 2 * Math.PI * x / xSize;
+ var x1 = 2 * Math.PI * y / ySize;
+ var value = 30 * Math.abs(Math.sin(x0 + w * fr)) * (Math.sin(x0 - w * fr) * Math.sin(8 * x0 + w * fr) * Math.cos(-8 * x1 - w * fr) + Math.abs(Math.sin(24 * x1)) / 3);
+ array[y * xSize + x] = value;
+ }
}
+
+ // build arguments to newHeatmapPainter( )
+ var data = { array: array, xSize: xSize, ySize: ySize, xMin: timeMin, xMax: timeMax, yMin: yMin, yMax: yMax };
+ var colorGradient = jet;
+ var colorScale = getGradientTexture(colorGradient);
+
+ // construct Pane and Painters
+ var plotPane = new Pane(newColumnLayout(), false);
+ plotPane.addPainter(newHeatmapPainter(rowAxis, colorAxis, data, colorScale, { blend: true }));
+ plotPane.addPainter(newEdgeAxisPainter(yAxis, Side.RIGHT, { showLabel: false, textColor: axisColor, tickColor: axisColor, tickSpacing: 34, tickSize: 5, font: '9px verdana,sans-serif' }));
+ colorAxisPane.addPainter(newEdgeAxisPainter(colorAxis, Side.RIGHT, { showLabel: false, textColor: axisColor, tickColor: axisColor, tickSpacing: 34, tickSize: 10, showBorder: true, gradientFill: colorGradient, font: '9px verdana,sans-serif' }));
+ plotPane.addPane(yAxisPane, 0);
+ plotPane.addPane(spacerPane, 1);
+ plotPane.addPane(colorInsetPane, 2);
+
+ return plotPane;
+
+ }
+
+ // Override content of an existing row
+ ui.rowUi('metsci.timelineExample.dynamicRow02').paneFactory = createHeatmapPlotPane;
+
+ // Example JSON Event loading
+ //
+ // Fetch timeline data from the server, and merge it into the existing timeline model
+ //
+ $.getJSON('timelineData.json', function (newTimeline: Timeline) {
+ model.merge(newTimeline, timelineMergeNewBeforeOld);
+
+ // once the timeline has been loaded, add listeners to some of the Panes
+ var pane = ui.rowUi('metsci.timelineExample.row03a').getPane('y-axis');
+ var clickListener = function (ev: PointerEvent) {
+ console.log('y-axis click: ' + ev.clickCount);
+ };
+ pane.mouseDown.on(clickListener);
+ });
+
+ // Example function for reloading TimelinePane with new TimelinePaneOptions. Takes care of
+ // removing the TimelinePane from its parent pane, disposing of its listeners/resources,
+ // creating a new TimelinePane, and reattaching it to the parent Pane. The new TimelinePane
+ // is returned.
+ //
+ // In this example, this function would be called as follows:
+ //
+ // timelinePane = reloadTimeline( contentPane, timelinePane, timelineOptions );
+ //
+ var reloadTimeline = function (parentPane: Pane, oldTimelinePane: TimelinePane, newOptions: TimelinePaneOptions): TimelinePane {
+
+ // remove the old TimelinePane from the parent Pane, dispose of it, and create a new TimelinePane
+ parentPane.removePane(oldTimelinePane);
+ oldTimelinePane.dispose.fire();
+ var reloadedTimelinePane = newTimelinePane(drawable, timeAxis, model, timelineOptions, oldTimelinePane.ui);
+ parentPane.addPane(reloadedTimelinePane);
+
+ // update the drawable
+ drawable.redraw();
+
+ // return the newly created Pane
+ return reloadedTimelinePane;
}
}
+
diff --git a/src/public_api.ts b/src/public_api.ts
new file mode 100644
index 0000000..450a74c
--- /dev/null
+++ b/src/public_api.ts
@@ -0,0 +1,64 @@
+// Util
+
+export * from './webglimpse/util/util';
+export * from './webglimpse/util/cache';
+export * from './webglimpse/util/multikey_cache';
+export * from './webglimpse/bounds';
+export * from './webglimpse/color';
+export * from './webglimpse/matrix';
+export * from './webglimpse/util/ordered_set';
+export * from './webglimpse/util/notification';
+export * from './webglimpse/util/sorted_arrays';
+export * from './webglimpse/util/binary_tree';
+export * from './webglimpse/util/sorted_multimap';
+export * from './webglimpse/util/split';
+
+
+// WebGL
+
+export * from './webglimpse/buffer';
+export * from './webglimpse/shader';
+export * from './webglimpse/texture';
+export * from './webglimpse/text';
+
+
+// Core
+
+export * from './webglimpse/core';
+export * from './webglimpse/misc';
+export * from './webglimpse/label';
+export * from './webglimpse/scroll';
+export * from './webglimpse/layout/inset_layout';
+export * from './webglimpse/layout/corner_layout';
+export * from './webglimpse/layout/row_layout';
+export * from './webglimpse/layout/column_layout';
+export * from './webglimpse/layout/overlay_layout';
+export * from './webglimpse/layout/card_layout';
+export * from './webglimpse/painter/border_painter';
+export * from './webglimpse/plot/axis';
+export * from './webglimpse/plot/plot_layout';
+export * from './webglimpse/plot/edge_axis_painter';
+export * from './webglimpse/plot/line_painter';
+export * from './webglimpse/gradient';
+export * from './webglimpse/plot/heatmap_painter';
+
+
+// Time
+
+export * from './webglimpse/time';
+export * from './webglimpse/timeline/time_axis';
+export * from './webglimpse/timeline/time_axis_painter';
+export * from './webglimpse/timeline/time_grid_painter';
+export * from './webglimpse/timeline/timeline_model';
+export * from './webglimpse/timeline/timeline_layout';
+export * from './webglimpse/timeline/timeline_cursor_painter';
+export * from './webglimpse/timeline/timeline_annotation_painter';
+export * from './webglimpse/timeline/timeline_annotation_style';
+export * from './webglimpse/timeline/timeline_event_style';
+export * from './webglimpse/timeline/timeline_ui';
+export * from './webglimpse/timeline/timeline_row';
+export * from './webglimpse/timeline/timeline_lanes';
+export * from './webglimpse/timeline/timeline_events_row';
+export * from './webglimpse/timeline/timeline_timeseries_row';
+export * from './webglimpse/timeline/timeline_styles';
+export * from './webglimpse/timeline/timeline_pane';
diff --git a/src/reference.ts b/src/reference.ts
deleted file mode 100644
index 76b6680..0000000
--- a/src/reference.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-// Util
-
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-
-
-// WebGL
-
-///
-///
-///
-///
-
-
-// Core
-
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-
-
-// Time
-
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-///
-
-
-
-// Examples
-
-///
-///
-///
-///
-///
-
-//grunt-start
-//grunt-end
\ No newline at end of file
diff --git a/src/webglimpse/bounds.ts b/src/webglimpse/bounds.ts
index 247f65a..46cb871 100644
--- a/src/webglimpse/bounds.ts
+++ b/src/webglimpse/bounds.ts
@@ -27,119 +27,118 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { xFrac, yFrac } from './core';
+import { clamp } from './util/util';
+
+export class BoundsUnmodifiable {
+ private bounds: Bounds;
+
+ constructor(bounds: Bounds) { this.bounds = bounds; }
+
+ get iStart(): number { return this.bounds.iStart; }
+ get jStart(): number { return this.bounds.jStart; }
+ get iEnd(): number { return this.bounds.iEnd; }
+ get jEnd(): number { return this.bounds.jEnd; }
+
+ get i(): number { return this.bounds.i; }
+ get j(): number { return this.bounds.j; }
+ get w(): number { return this.bounds.w; }
+ get h(): number { return this.bounds.h; }
+
+ xFrac(i: number): number { return this.bounds.xFrac(i); }
+ yFrac(j: number): number { return this.bounds.yFrac(j); }
+ contains(i: number, j: number): boolean { return this.bounds.contains(i, j); }
+}
+
+
+export class Bounds {
+ private _iStart: number;
+ private _jStart: number;
+ private _iEnd: number;
+ private _jEnd: number;
+ private _unmod: BoundsUnmodifiable;
+
+ constructor() {
+ this._iStart = 0;
+ this._jStart = 0;
+ this._iEnd = 0;
+ this._jEnd = 0;
+ this._unmod = new BoundsUnmodifiable(this);
+ }
+ get iStart(): number { return this._iStart; }
+ get jStart(): number { return this._jStart; }
+ get iEnd(): number { return this._iEnd; }
+ get jEnd(): number { return this._jEnd; }
- export class BoundsUnmodifiable {
- private bounds : Bounds;
+ get i(): number { return this._iStart; }
+ get j(): number { return this._jStart; }
+ get w(): number { return this._iEnd - this._iStart; }
+ get h(): number { return this._jEnd - this._jStart; }
- constructor( bounds : Bounds ) { this.bounds = bounds; }
+ get unmod(): BoundsUnmodifiable { return this._unmod; }
- get iStart( ) : number { return this.bounds.iStart; }
- get jStart( ) : number { return this.bounds.jStart; }
- get iEnd( ) : number { return this.bounds.iEnd; }
- get jEnd( ) : number { return this.bounds.jEnd; }
+ xFrac(i: number): number {
+ return (i - this._iStart) / (this._iEnd - this._iStart);
+ }
- get i( ) : number { return this.bounds.i; }
- get j( ) : number { return this.bounds.j; }
- get w( ) : number { return this.bounds.w; }
- get h( ) : number { return this.bounds.h; }
+ yFrac(j: number): number {
+ return (j - this._jStart) / (this._jEnd - this._jStart);
+ }
- xFrac( i : number ) : number { return this.bounds.xFrac( i ); }
- yFrac( j : number ) : number { return this.bounds.yFrac( j ); }
- contains( i : number, j : number ) : boolean { return this.bounds.contains( i, j ); }
+ contains(i: number, j: number): boolean {
+ return (this._iStart <= i && i < this._iEnd && this._jStart <= j && j < this._jEnd);
}
+ setEdges(iStart: number, iEnd: number, jStart: number, jEnd: number) {
+ this._iStart = iStart;
+ this._jStart = jStart;
+ this._iEnd = iEnd;
+ this._jEnd = jEnd;
+ }
- export class Bounds {
- private _iStart : number;
- private _jStart : number;
- private _iEnd : number;
- private _jEnd : number;
- private _unmod : BoundsUnmodifiable;
-
- constructor( ) {
- this._iStart = 0;
- this._jStart = 0;
- this._iEnd = 0;
- this._jEnd = 0;
- this._unmod = new BoundsUnmodifiable( this );
- }
-
- get iStart( ) : number { return this._iStart; }
- get jStart( ) : number { return this._jStart; }
- get iEnd( ) : number { return this._iEnd; }
- get jEnd( ) : number { return this._jEnd; }
-
- get i( ) : number { return this._iStart; }
- get j( ) : number { return this._jStart; }
- get w( ) : number { return this._iEnd - this._iStart; }
- get h( ) : number { return this._jEnd - this._jStart; }
-
- get unmod( ) : BoundsUnmodifiable { return this._unmod; }
-
- xFrac( i : number ) : number {
- return ( i - this._iStart ) / ( this._iEnd - this._iStart );
- }
-
- yFrac( j : number ) : number {
- return ( j - this._jStart ) / ( this._jEnd - this._jStart );
- }
-
- contains( i : number, j : number ) : boolean {
- return ( this._iStart <= i && i < this._iEnd && this._jStart <= j && j < this._jEnd );
- }
-
- setEdges( iStart : number, iEnd : number, jStart : number, jEnd : number ) {
- this._iStart = iStart;
- this._jStart = jStart;
- this._iEnd = iEnd;
- this._jEnd = jEnd;
- }
-
- setRect( i : number, j : number, w : number, h : number ) {
- this.setEdges( i, i+w, j, j+h );
- }
-
- setBounds( bounds : BoundsUnmodifiable ) {
- this.setEdges( bounds.iStart, bounds.iEnd, bounds.jStart, bounds.jEnd );
- }
-
- cropToEdges( iCropStart : number, iCropEnd : number, jCropStart : number, jCropEnd : number ) {
- this._iStart = clamp( iCropStart, iCropEnd, this._iStart );
- this._jStart = clamp( jCropStart, jCropEnd, this._jStart );
- this._iEnd = clamp( iCropStart, iCropEnd, this._iEnd );
- this._jEnd = clamp( jCropStart, jCropEnd, this._jEnd );
- }
-
- cropToRect( iCrop : number, jCrop : number, wCrop : number, hCrop : number ) {
- this.cropToEdges( iCrop, iCrop+wCrop, jCrop, jCrop+hCrop );
- }
-
- cropToBounds( cropBounds : BoundsUnmodifiable ) {
- this.cropToEdges( cropBounds.iStart, cropBounds.iEnd, cropBounds.jStart, cropBounds.jEnd );
- }
+ setRect(i: number, j: number, w: number, h: number) {
+ this.setEdges(i, i + w, j, j + h);
}
+ setBounds(bounds: BoundsUnmodifiable) {
+ this.setEdges(bounds.iStart, bounds.iEnd, bounds.jStart, bounds.jEnd);
+ }
- export function newBoundsFromRect( i : number, j : number, w : number, h : number ) : Bounds {
- var b = new Bounds( );
- b.setRect( i, j, w, h );
- return b;
+ cropToEdges(iCropStart: number, iCropEnd: number, jCropStart: number, jCropEnd: number) {
+ this._iStart = clamp(iCropStart, iCropEnd, this._iStart);
+ this._jStart = clamp(jCropStart, jCropEnd, this._jStart);
+ this._iEnd = clamp(iCropStart, iCropEnd, this._iEnd);
+ this._jEnd = clamp(jCropStart, jCropEnd, this._jEnd);
}
+ cropToRect(iCrop: number, jCrop: number, wCrop: number, hCrop: number) {
+ this.cropToEdges(iCrop, iCrop + wCrop, jCrop, jCrop + hCrop);
+ }
- export function newBoundsFromEdges( iStart : number, iEnd : number, jStart : number, jEnd : number ) : Bounds {
- var b = new Bounds( );
- b.setEdges( iStart, iEnd, jStart, jEnd );
- return b;
+ cropToBounds(cropBounds: BoundsUnmodifiable) {
+ this.cropToEdges(cropBounds.iStart, cropBounds.iEnd, cropBounds.jStart, cropBounds.jEnd);
}
+}
- export interface Size {
- w : number;
- h : number;
- }
+export function newBoundsFromRect(i: number, j: number, w: number, h: number): Bounds {
+ const b = new Bounds();
+ b.setRect(i, j, w, h);
+ return b;
+}
+
+
+export function newBoundsFromEdges(iStart: number, iEnd: number, jStart: number, jEnd: number): Bounds {
+ const b = new Bounds();
+ b.setEdges(iStart, iEnd, jStart, jEnd);
+ return b;
+}
+
+
+export interface Size {
+ w: number;
+ h: number;
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/buffer.ts b/src/webglimpse/buffer.ts
index 6017d99..af80d0f 100644
--- a/src/webglimpse/buffer.ts
+++ b/src/webglimpse/buffer.ts
@@ -27,142 +27,142 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { StringMap, GL, getObjectId, hasval } from './util/util';
+export interface Buffer {
+ bind(gl: WebGLRenderingContext, target: number): void;
+ unbind(gl: WebGLRenderingContext, target: number): void;
+ dispose(): void;
+}
- export interface Buffer {
- bind( gl : WebGLRenderingContext, target : number );
- unbind( gl : WebGLRenderingContext, target : number );
- dispose( );
- }
+export interface DynamicBuffer extends Buffer {
+ setData(newData: Float32Array): void;
+}
- export interface DynamicBuffer extends Buffer {
- setData( newData : ArrayBuffer );
- }
+export function newStaticBuffer(data: Float32Array): Buffer {
+ return new StaticBufferImpl(data);
+}
- export function newStaticBuffer( data : ArrayBuffer ) : Buffer {
- return new StaticBufferImpl( data );
- }
+export function newDynamicBuffer(data: Float32Array = new Float32Array(0)): DynamicBuffer {
+ return new DynamicBufferImpl(data);
+}
+
+
+class BufferEntry {
+ gl: WebGLRenderingContext;
+ buffer: WebGLBuffer;
+ capacity = 0;
+ marker: number = null;
- export function newDynamicBuffer( data : ArrayBuffer = new Float32Array( 0 ) ) : DynamicBuffer {
- return new DynamicBufferImpl( data );
+ constructor(gl: WebGLRenderingContext, buffer: WebGLBuffer) {
+ this.gl = gl;
+ this.buffer = buffer;
}
+}
+class AbstractBuffer implements Buffer {
+ private buffers: StringMap = {};
+ private currentMarker = 0;
- class BufferEntry {
- gl : WebGLRenderingContext;
- buffer : WebGLBuffer;
- capacity : number = 0;
- marker : number = null;
+ init(gl: WebGLRenderingContext, target: number): number {
+ throw new Error('Method is abstract');
+ }
- constructor( gl : WebGLRenderingContext, buffer : WebGLBuffer ) {
- this.gl = gl;
- this.buffer = buffer;
- }
+ update(gl: WebGLRenderingContext, target: number, capacity: number): number {
+ throw new Error('Method is abstract');
}
- class AbstractBuffer implements Buffer {
- private buffers : StringMap = { };
- private currentMarker : number = 0;
+ setDirty() {
+ this.currentMarker++;
+ }
- init( gl : WebGLRenderingContext, target : number ) : number {
- throw new Error( 'Method is abstract' );
- }
+ bind(gl: WebGLRenderingContext, target: number) {
+ const glId = getObjectId(gl);
+ if (this.buffers[glId] === undefined) {
+ const buffer = gl.createBuffer();
+ if (!hasval(buffer)) {
+ throw new Error('Failed to create buffer');
+ }
+ this.buffers[glId] = new BufferEntry(gl, buffer);
- update( gl : WebGLRenderingContext, target : number, capacity : number ) : number {
- throw new Error( 'Method is abstract' );
+ gl.bindBuffer(target, this.buffers[glId].buffer);
+ this.buffers[glId].capacity = this.init(gl, target);
+ this.buffers[glId].marker = this.currentMarker;
}
-
- setDirty( ) {
- this.currentMarker++;
+ else if (this.buffers[glId].marker !== this.currentMarker) {
+ gl.bindBuffer(target, this.buffers[glId].buffer);
+ this.buffers[glId].capacity = this.update(gl, target, this.buffers[glId].capacity);
+ this.buffers[glId].marker = this.currentMarker;
}
-
- bind( gl : WebGLRenderingContext, target : number ) {
- var glId = getObjectId( gl );
- if ( this.buffers[ glId ] === undefined ) {
- var buffer = gl.createBuffer( );
- if ( !hasval( buffer ) ) throw new Error( 'Failed to create buffer' );
- this.buffers[ glId ] = new BufferEntry( gl, buffer );
-
- gl.bindBuffer( target, this.buffers[ glId ].buffer );
- this.buffers[ glId ].capacity = this.init( gl, target );
- this.buffers[ glId ].marker = this.currentMarker;
- }
- else if ( this.buffers[ glId ].marker !== this.currentMarker ) {
- gl.bindBuffer( target, this.buffers[ glId ].buffer );
- this.buffers[ glId ].capacity = this.update( gl, target, this.buffers[ glId ].capacity );
- this.buffers[ glId ].marker = this.currentMarker;
- }
- else {
- gl.bindBuffer( target, this.buffers[ glId ].buffer );
- }
+ else {
+ gl.bindBuffer(target, this.buffers[glId].buffer);
}
+ }
- unbind( gl : WebGLRenderingContext, target : number ) {
- gl.bindBuffer( target, null );
- }
+ unbind(gl: WebGLRenderingContext, target: number) {
+ gl.bindBuffer(target, null);
+ }
- dispose( ) {
- // XXX: Not sure this actually works ... may have to make each gl current or something
- for ( var glid in this.buffers ) {
- if ( this.buffers.hasOwnProperty( glid ) ) {
- var en = this.buffers[ glid ];
- en.gl.deleteBuffer( en.buffer );
- }
+ dispose() {
+ // XXX: Not sure this actually works ... may have to make each gl current or something
+ for (const glid in this.buffers) {
+ if (this.buffers.hasOwnProperty(glid)) {
+ const en = this.buffers[glid];
+ en.gl.deleteBuffer(en.buffer);
}
- this.buffers = { };
}
+ this.buffers = {};
}
+}
- class StaticBufferImpl extends AbstractBuffer {
- private _data : ArrayBuffer;
+class StaticBufferImpl extends AbstractBuffer {
+ private _data: Float32Array;
- constructor( data : ArrayBuffer ) {
- super( );
- this._data = data;
- }
+ constructor(data: Float32Array) {
+ super();
+ this._data = data;
+ }
- init( gl : WebGLRenderingContext, target : number ) : number {
- gl.bufferData( target, this._data, GL.STATIC_DRAW );
- return this._data.byteLength;
- }
+ init(gl: WebGLRenderingContext, target: number): number {
+ gl.bufferData(target, this._data, GL.STATIC_DRAW);
+ return this._data.byteLength;
+ }
- update( gl : WebGLRenderingContext, target : number, capacity : number ) : number {
- throw new Error( 'This buffer is static and should never need to be updated' );
- }
+ update(gl: WebGLRenderingContext, target: number, capacity: number): number {
+ throw new Error('This buffer is static and should never need to be updated');
}
+}
- class DynamicBufferImpl extends AbstractBuffer implements DynamicBuffer {
- private _data : ArrayBuffer;
+class DynamicBufferImpl extends AbstractBuffer implements DynamicBuffer {
+ private _data: Float32Array;
- constructor( data : ArrayBuffer ) {
- super( );
- this._data = data;
- }
+ constructor(data: Float32Array) {
+ super();
+ this._data = data;
+ }
- setData( data : ArrayBuffer ) {
- this._data = data;
- this.setDirty( );
- }
+ setData(data: Float32Array) {
+ this._data = data;
+ this.setDirty();
+ }
- init( gl : WebGLRenderingContext, target : number ) : number {
- gl.bufferData( target, this._data, GL.DYNAMIC_DRAW );
- return this._data.byteLength;
- }
+ init(gl: WebGLRenderingContext, target: number): number {
+ gl.bufferData(target, this._data, GL.DYNAMIC_DRAW);
+ return this._data.byteLength;
+ }
- update( gl : WebGLRenderingContext, target : number, capacity : number ) : number {
- if ( this._data.byteLength <= capacity ) {
- gl.bufferSubData( target, 0, this._data );
- return capacity;
- }
- else {
- gl.bufferData( target, this._data, GL.DYNAMIC_DRAW );
- return this._data.byteLength;
- }
+ update(gl: WebGLRenderingContext, target: number, capacity: number): number {
+ if (this._data.byteLength <= capacity) {
+ gl.bufferSubData(target, 0, this._data);
+ return capacity;
+ }
+ else {
+ gl.bufferData(target, this._data, GL.DYNAMIC_DRAW);
+ return this._data.byteLength;
}
}
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/color.ts b/src/webglimpse/color.ts
index 06c0de8..ffeefc8 100644
--- a/src/webglimpse/color.ts
+++ b/src/webglimpse/color.ts
@@ -27,120 +27,123 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
-
- export class Color {
- private _r : number;
- private _g : number;
- private _b : number;
- private _a : number;
-
- get r( ) : number { return this._r; }
- get g( ) : number { return this._g; }
- get b( ) : number { return this._b; }
- get a( ) : number { return this._a; }
-
- get cssString( ) : string {
- return 'rgba(' + Math.round( 255*this._r ) + ',' + Math.round( 255*this._g ) + ',' + Math.round( 255*this._b ) + ',' + this._a + ')';
- }
-
- get rgbaString( ) : string {
- return '' + Math.round( 255*this._r ) + ',' + Math.round( 255*this._g ) + ',' + Math.round( 255*this._b ) + ',' + Math.round( 255*this._a );
- }
-
- constructor( r : number, g : number, b : number, a : number = 1 ) {
- this._r = r;
- this._g = g;
- this._b = b;
- this._a = a;
- }
-
- withAlphaTimes( aFactor : number ) : Color {
- return new Color( this._r, this._g, this._b, aFactor * this._a );
- }
- }
+import { hasval } from './util/util';
+export class Color {
+ private _r: number;
+ private _g: number;
+ private _b: number;
+ private _a: number;
- export function darker( color : Color, factor : number ) : Color {
- return new Color( color.r * factor, color.g * factor, color.b * factor, color.a );
- }
+ get r(): number { return this._r; }
+ get g(): number { return this._g; }
+ get b(): number { return this._b; }
+ get a(): number { return this._a; }
+ get cssString(): string {
+ return 'rgba(' + Math.round(255 * this._r) + ',' + Math.round(255 * this._g) + ',' + Math.round(255 * this._b) + ',' + this._a + ')';
+ }
- export function rgba( r : number, g : number, b : number, a : number ) : Color {
- return new Color( r, g, b, a );
+ get rgbaString(): string {
+ return '' + Math.round(255 * this._r) + ',' + Math.round(255 * this._g) + ',' + Math.round(255 * this._b) + ',' + Math.round(255 * this._a);
}
+ constructor(r: number, g: number, b: number, a: number = 1) {
+ this._r = r;
+ this._g = g;
+ this._b = b;
+ this._a = a;
+ }
- export function rgb( r : number, g : number, b : number ) : Color {
- return new Color( r, g, b, 1 );
+ withAlphaTimes(aFactor: number): Color {
+ return new Color(this._r, this._g, this._b, aFactor * this._a);
}
+}
- export function sameColor( c1 : Color, c2 : Color ) : boolean {
- if ( c1 === c2 ) return true;
- if ( !hasval( c1 ) || !hasval( c2 ) ) return false;
- return ( c1.r === c2.r && c1.g === c2.g && c1.b === c2.b && c1.a === c2.a );
- }
+export function darker(color: Color, factor: number): Color {
+ return new Color(color.r * factor, color.g * factor, color.b * factor, color.a);
+}
- export function parseRgba( rgbaString : string ) : Color {
- var tokens = rgbaString.split( ',', 4 );
- return new Color( parseInt(tokens[0])/255, parseInt(tokens[1])/255, parseInt(tokens[2])/255, parseInt(tokens[3])/255 );
- }
+export function rgba(r: number, g: number, b: number, a: number): Color {
+ return new Color(r, g, b, a);
+}
+
+
+export function rgb(r: number, g: number, b: number): Color {
+ return new Color(r, g, b, 1);
+}
- /**
- * Creates a Color object based on a CSS color string. Supports the following notations:
- * - hex
- * - rgb/rgba
- * - hsl/hsla
- * - named colors
- *
- * Behavior is undefined for strings that are not in one of the listed notations.
- *
- * Note that different browsers may use different color values for the named colors.
- *
- */
- export var parseCssColor = ( function( ) {
- var canvas = document.createElement( 'canvas' );
- canvas.width = 1;
- canvas.height = 1;
- var g = canvas.getContext( '2d' );
- return function( cssColorString : string ) : Color {
- g.clearRect( 0, 0, 1, 1 );
- g.fillStyle = cssColorString;
- g.fillRect( 0, 0, 1, 1 );
-
- var rgbaData = g.getImageData( 0, 0, 1, 1 ).data;
- var R = rgbaData[ 0 ] / 255;
- var G = rgbaData[ 1 ] / 255;
- var B = rgbaData[ 2 ] / 255;
- var A = rgbaData[ 3 ] / 255;
- return rgba( R, G, B, A );
- }
- } )( );
-
-
- export function gray( brightness : number ) : Color {
- return new Color( brightness, brightness, brightness, 1 );
+export function sameColor(c1: Color, c2: Color): boolean {
+ if (c1 === c2) {
+ return true;
}
+ if (!hasval(c1) || !hasval(c2)) {
+ return false;
+ }
+ return (c1.r === c2.r && c1.g === c2.g && c1.b === c2.b && c1.a === c2.a);
+}
+
+
+export function parseRgba(rgbaString: string): Color {
+ const tokens = rgbaString.split(',', 4);
+ return new Color(parseInt(tokens[0], 10) / 255, parseInt(tokens[1], 10) / 255, parseInt(tokens[2], 10) / 255, parseInt(tokens[3], 10) / 255);
+}
+
+
+/**
+ * Creates a Color object based on a CSS color string. Supports the following notations:
+ * - hex
+ * - rgb/rgba
+ * - hsl/hsla
+ * - named colors
+ * Behavior is undefined for strings that are not in one of the listed notations.
+ *
+ * Optional alphaOverride provided for forcing value of rbga alpha.
+ *
+ * Note that different browsers may use different color values for the named colors.
+ *
+ */
+export let parseCssColor = (function () {
+ const canvas = document.createElement('canvas');
+ canvas.width = 1;
+ canvas.height = 1;
+ const g = canvas.getContext('2d', { willReadFrequently: true});
+ return function (cssColorString: string, alphaOverride?: number): Color {
+ g.clearRect(0, 0, 1, 1);
+ g.fillStyle = cssColorString;
+ g.fillRect(0, 0, 1, 1);
+
+ const rgbaData = g.getImageData(0, 0, 1, 1).data;
+ const R = rgbaData[0] / 255;
+ const G = rgbaData[1] / 255;
+ const B = rgbaData[2] / 255;
+ const A = hasval(alphaOverride) ? alphaOverride : rgbaData[3] / 255;
+ return rgba(R, G, B, A);
+ };
+})();
+
+
+export function gray(brightness: number): Color {
+ return new Color(brightness, brightness, brightness, 1);
+}
- // XXX: Make final
+// XXX: Make final
- export var black = rgb( 0, 0, 0 );
- export var white = rgb( 1, 1, 1 );
+export let black = rgb(0, 0, 0);
+export let white = rgb(1, 1, 1);
- export var red = rgb( 1, 0, 0 );
- export var green = rgb( 0, 1, 0 );
- export var blue = rgb( 0, 0, 1 );
+export let red = rgb(1, 0, 0);
+export let green = rgb(0, 1, 0);
+export let blue = rgb(0, 0, 1);
- export var cyan = rgb( 0, 1, 1 );
- export var magenta = rgb( 1, 0, 1 );
- export var yellow = rgb( 1, 1, 0 );
+export let cyan = rgb(0, 1, 1);
+export let magenta = rgb(1, 0, 1);
+export let yellow = rgb(1, 1, 0);
- export var periwinkle = rgb( 0.561, 0.561, 0.961 );
+export let periwinkle = rgb(0.561, 0.561, 0.961);
-}
\ No newline at end of file
diff --git a/src/webglimpse/core.ts b/src/webglimpse/core.ts
index de1a61b..6ff1c6c 100644
--- a/src/webglimpse/core.ts
+++ b/src/webglimpse/core.ts
@@ -27,760 +27,757 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { BoundsUnmodifiable, Bounds, Size, newBoundsFromRect } from './bounds';
+import { Notification, Listener, Notification1 } from './util/notification';
+import { OrderedSet } from './util/ordered_set';
+import { alwaysTrue, GL, getObjectId, hasval } from './util/util';
+export type Painter = (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) => void;
- export interface Painter {
- ( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable );
- }
+export interface PointerEvent {
+ paneViewport: BoundsUnmodifiable;
+ i: number;
+ j: number;
+ wheelSteps?: number;
+ clickCount?: number;
+ mouseEvent?: MouseEvent;
+}
- export interface PointerEvent {
- paneViewport : BoundsUnmodifiable;
- i : number;
- j : number;
- wheelSteps? : number;
- clickCount? : number;
- mouseEvent? : MouseEvent;
- }
+export function xFrac(ev: PointerEvent) {
+ return ev.paneViewport.xFrac(ev.i);
+}
- export function xFrac( ev : PointerEvent ) {
- return ev.paneViewport.xFrac( ev.i );
- }
+export function yFrac(ev: PointerEvent) {
+ return ev.paneViewport.yFrac(ev.j);
+}
- export function yFrac( ev : PointerEvent ) {
- return ev.paneViewport.yFrac( ev.j );
- }
+export interface LayoutEntry {
+ layoutArg: any;
+ layoutOptions: any;
+ viewport: Bounds;
+ prefSize: Size;
+}
- export interface LayoutEntry {
- layoutArg : any;
- layoutOptions : any;
- viewport : Bounds;
- prefSize : Size;
- }
+export type LayoutPhase1 = (parentPrefSize: Size, children?: LayoutEntry[]) => LayoutPhase1;
- export interface LayoutPhase1 {
- ( parentPrefSize : Size, children : LayoutEntry[] );
- }
+export type LayoutPhase2 = (children: LayoutEntry[], parentViewport: BoundsUnmodifiable) => LayoutPhase2;
- export interface LayoutPhase2 {
- ( children : LayoutEntry[], parentViewport : BoundsUnmodifiable );
- }
+export interface Layout {
+ updatePrefSize?: LayoutPhase1;
+ updateChildViewports?: LayoutPhase2;
+}
- export interface Layout {
- updatePrefSize? : LayoutPhase1;
- updateChildViewports? : LayoutPhase2;
- }
+
+export type Mask2D = (viewport: BoundsUnmodifiable, i: number, j: number) => boolean;
- export interface Mask2D {
- ( viewport : BoundsUnmodifiable, i : number, j : number ) : boolean;
+class PaneChild implements LayoutEntry {
+ pane: Pane;
+ layoutArg: any;
+ layoutOptions: any;
+ viewport: Bounds;
+ scissor: Bounds;
+ prefSize: Size;
+
+ constructor(pane: Pane, layoutArg: any, layoutOptions: any) {
+ this.pane = pane;
+ this.layoutArg = layoutArg;
+ this.layoutOptions = layoutOptions;
+ this.viewport = newBoundsFromRect(0, 0, 0, 0);
+ this.scissor = newBoundsFromRect(0, 0, 0, 0);
+ this.prefSize = { w: 0, h: 0 };
}
+}
- class PaneChild implements LayoutEntry {
- pane : Pane;
- layoutArg : any;
- layoutOptions : any;
- viewport : Bounds;
- scissor : Bounds;
- prefSize : Size;
+export class Pane {
+ private painters: Painter[];
+ private consumesInputEvents: boolean;
+ private isInside: Mask2D;
- constructor( pane : Pane, layoutArg : any, layoutOptions : any ) {
- this.pane = pane;
- this.layoutArg = layoutArg;
- this.layoutOptions = layoutOptions;
- this.viewport = newBoundsFromRect( 0, 0, 0, 0 );
- this.scissor = newBoundsFromRect( 0, 0, 0, 0 );
- this.prefSize = { w: 0, h: 0 };
- }
- }
+ private _mouseCursor: string;
+ private _mouseCursorChanged: Notification;
+ private _childMouseCursorListener: Listener;
+ private children: OrderedSet;
+ private _layout: Layout;
+ private _viewport: Bounds;
+ private _scissor: Bounds;
+ private _viewportChanged: Notification;
- export class Pane {
- private painters : Painter[];
- private consumesInputEvents : boolean;
- private isInside : Mask2D;
+ private _dispose: Notification;
- private _mouseCursor : string;
- private _mouseCursorChanged : Notification;
- private _childMouseCursorListener : Listener;
+ // Input Handling
+ //
- private children : OrderedSet;
- private _layout : Layout;
- private _viewport : Bounds;
- private _scissor : Bounds;
- private _viewportChanged : Notification;
-
- private _dispose : Notification;
+ private _mouseUp: Notification1 = new Notification1();
+ private _mouseDown: Notification1 = new Notification1();
+ private _mouseMove: Notification1 = new Notification1();
+ private _mouseWheel: Notification1 = new Notification1();
+ private _mouseEnter: Notification1 = new Notification1();
+ private _mouseExit: Notification1 = new Notification1();
+ private _contextMenu: Notification1 = new Notification1();
+
+ constructor(layout: Layout, consumesInputEvents: boolean = true, isInside: Mask2D = alwaysTrue) {
+ this.painters = [];
+ this.consumesInputEvents = consumesInputEvents;
+ this.isInside = isInside;
+
+ this._mouseCursor = (consumesInputEvents ? 'default' : null);
+ this._mouseCursorChanged = new Notification();
+ this._childMouseCursorListener = (() => this._mouseCursorChanged.fire());
+
+ this.children = new OrderedSet([], (paneChild) => getObjectId(paneChild.pane), false);
+ this._layout = layout;
+ this._viewport = newBoundsFromRect(0, 0, 0, 0);
+ this._scissor = newBoundsFromRect(0, 0, 0, 0);
+ this._viewportChanged = new Notification();
+
+ this._dispose = new Notification();
+
+ this._dispose.on(() => {
+ this._mouseUp.dispose();
+ this._mouseDown.dispose();
+ this._mouseMove.dispose();
+ this._mouseWheel.dispose();
+ this._mouseEnter.dispose();
+ this._mouseExit.dispose();
+ this._contextMenu.dispose();
+
+ // recursively dispose all child panes
+ for (let i = 0; i < this.children.length; i++) {
+ this.children.valueAt(i).pane.dispose.fire();
+ }
+ });
+ }
- constructor( layout : Layout, consumesInputEvents : boolean = true, isInside : Mask2D = alwaysTrue ) {
- this.painters = [];
- this.consumesInputEvents = consumesInputEvents;
- this.isInside = isInside;
+ get layout(): Layout {
+ return this._layout;
+ }
- this._mouseCursor = ( consumesInputEvents ? 'default' : null );
- this._mouseCursorChanged = new Notification( );
- this._childMouseCursorListener = ( ( ) => this._mouseCursorChanged.fire( ) );
+ set layout(layout: Layout) {
+ this._layout = layout;
+ }
- this.children = new OrderedSet( [], (paneChild)=>getObjectId(paneChild.pane), false );
- this._layout = layout;
- this._viewport = newBoundsFromRect( 0, 0, 0, 0 );
- this._scissor = newBoundsFromRect( 0, 0, 0, 0 );
- this._viewportChanged = new Notification( );
-
- this._dispose = new Notification( );
-
- this._dispose.on( ( ) => {
- this._mouseUp.dispose( );
- this._mouseDown.dispose( );
- this._mouseMove.dispose( );
- this._mouseWheel.dispose( );
- this._mouseEnter.dispose( );
- this._mouseExit.dispose( );
- this._contextMenu.dispose( );
-
- // recursively dispose all child panes
- for ( var i = 0 ; i < this.children.length ; i++ ) {
- this.children.valueAt( i ).pane.dispose.fire( );
- }
- } );
- }
-
- get layout( ) : Layout {
- return this.layout;
- }
-
- set layout( layout : Layout ) {
- this._layout = layout;
- }
+ get mouseCursor(): string {
+ return this._mouseCursor;
+ }
- get mouseCursor( ) : string {
- return this._mouseCursor;
+ set mouseCursor(mouseCursor: string) {
+ if (mouseCursor !== this._mouseCursor) {
+ this._mouseCursor = mouseCursor;
+ this._mouseCursorChanged.fire();
}
+ }
- set mouseCursor( mouseCursor : string ) {
- if ( mouseCursor !== this._mouseCursor ) {
- this._mouseCursor = mouseCursor;
- this._mouseCursorChanged.fire( );
- }
- }
+ get mouseCursorChanged(): Notification {
+ return this._mouseCursorChanged;
+ }
- get mouseCursorChanged( ) : Notification {
- return this._mouseCursorChanged;
- }
+ addPainter(painter: Painter) {
+ this.painters.push(painter);
+ }
- addPainter( painter : Painter ) {
- this.painters.push( painter );
- }
+ addPane(pane: Pane, layoutArg: any = null, layoutOptions: any = {}) {
+ this.children.add(new PaneChild(pane, layoutArg, layoutOptions));
+ pane.mouseCursorChanged.on(this._childMouseCursorListener);
+ }
- addPane( pane : Pane, layoutArg : any = null, layoutOptions : any = { } ) {
- this.children.add( new PaneChild( pane, layoutArg, layoutOptions ) );
- pane.mouseCursorChanged.on( this._childMouseCursorListener );
- }
+ removePane(pane: Pane) {
+ this.children.removeValue(this._childFor(pane));
+ pane.mouseCursorChanged.off(this._childMouseCursorListener);
+ }
- removePane( pane : Pane ) {
- this.children.removeValue( this._childFor( pane ) );
- pane.mouseCursorChanged.off( this._childMouseCursorListener );
- }
+ layoutArg(pane: Pane): any {
+ return this._childFor(pane).layoutArg;
+ }
- layoutArg( pane : Pane ) : any {
- return this._childFor( pane ).layoutArg;
- }
+ setLayoutArg(pane: Pane, layoutArg: any) {
+ this._childFor(pane).layoutArg = layoutArg;
+ }
- setLayoutArg( pane : Pane, layoutArg : any ) {
- this._childFor( pane ).layoutArg = layoutArg;
+ updateLayoutArgs(updateFn: (layoutArg: any, layoutOptions: any) => any) {
+ for (let c = 0; c < this.children.length; c++) {
+ const child = this.children.valueAt(c);
+ child.layoutArg = updateFn(child.layoutArg, child.layoutOptions);
}
+ }
- updateLayoutArgs( updateFn : (layoutArg:any,layoutOptions:any)=>any ) {
- for ( var c = 0; c < this.children.length; c++ ) {
- var child = this.children.valueAt( c );
- child.layoutArg = updateFn( child.layoutArg, child.layoutOptions );
- }
- }
+ layoutOptions(pane: Pane): any {
+ return this._childFor(pane).layoutOptions;
+ }
- layoutOptions( pane : Pane ) : any {
- return this._childFor( pane ).layoutOptions;
- }
+ private _childFor(pane: Pane): PaneChild {
+ return this.children.valueFor(getObjectId(pane));
+ }
- private _childFor( pane : Pane ) : PaneChild {
- return this.children.valueFor( getObjectId( pane ) );
- }
+ // Layout & Paint
+ //
- // Layout & Paint
- //
+ updatePrefSizes(result: Size) {
+ // Child panes
+ for (let c = 0; c < this.children.length; c++) {
+ const child = this.children.valueAt(c);
+ child.pane.updatePrefSizes(child.prefSize);
+ }
+ // This pane
+ if (hasval(this._layout) && hasval(this._layout.updatePrefSize)) {
+ this._layout.updatePrefSize(result, this.children.toArray());
+ }
+ else {
+ result.w = null;
+ result.h = null;
+ }
+ }
- updatePrefSizes( result : Size ) {
- // Child panes
- for ( var c = 0; c < this.children.length; c++ ) {
- var child = this.children.valueAt( c );
- child.pane.updatePrefSizes( child.prefSize );
- }
- // This pane
- if ( hasval( this._layout ) && hasval( this._layout.updatePrefSize ) ) {
- this._layout.updatePrefSize( result, this.children.toArray( ) );
- }
- else {
- result.w = null;
- result.h = null;
+ updateBounds(viewport: BoundsUnmodifiable, scissor: BoundsUnmodifiable) {
+ // This pane
+ const viewportChanged = (viewport.iStart !== this._viewport.iStart || viewport.iEnd !== this._viewport.iEnd || viewport.jStart !== this._viewport.jStart || viewport.jEnd !== this._viewport.jEnd);
+ this._viewport.setBounds(viewport);
+ this._scissor.setBounds(scissor);
+ // Child panes
+ if (hasval(this._layout) && hasval(this._layout.updateChildViewports)) {
+ this._layout.updateChildViewports(this.children.toArray(), viewport);
+ for (let c = 0; c < this.children.length; c++) {
+ const child = this.children.valueAt(c);
+ child.scissor.setBounds(child.viewport.unmod);
+ child.scissor.cropToBounds(scissor);
+ child.pane.updateBounds(child.viewport.unmod, child.scissor.unmod);
}
}
+ else if (this.children.length > 0) {
+ throw new Error('Pane has ' + this.children.length + ' ' + (this.children.length === 1 ? 'child' : 'children') + ', but its layout is ' + this.layout);
+ }
+ // Notify
+ if (viewportChanged) {
+ this._viewportChanged.fire();
+ }
+ }
- updateBounds( viewport : BoundsUnmodifiable, scissor : BoundsUnmodifiable ) {
+ paint(gl: WebGLRenderingContext) {
+ const viewport = this._viewport.unmod;
+ const scissor = this._scissor.unmod;
+ if (scissor.w > 0 && scissor.h > 0) {
// This pane
- var viewportChanged = ( viewport.iStart !== this._viewport.iStart || viewport.iEnd !== this._viewport.iEnd || viewport.jStart !== this._viewport.jStart || viewport.jEnd !== this._viewport.jEnd );
- this._viewport.setBounds( viewport );
- this._scissor.setBounds( scissor );
- // Child panes
- if ( hasval( this._layout ) && hasval( this._layout.updateChildViewports ) ) {
- this._layout.updateChildViewports( this.children.toArray( ), viewport );
- for ( var c = 0; c < this.children.length; c++ ) {
- var child = this.children.valueAt( c );
- child.scissor.setBounds( child.viewport.unmod );
- child.scissor.cropToBounds( scissor );
- child.pane.updateBounds( child.viewport.unmod, child.scissor.unmod );
- }
- }
- else if ( this.children.length > 0 ) {
- throw new Error( 'Pane has ' + this.children.length + ' ' + ( this.children.length === 1 ? 'child' : 'children' ) + ', but its layout is ' + this.layout );
+ gl.viewport(viewport.i, viewport.j, viewport.w, viewport.h);
+ gl.enable(GL.SCISSOR_TEST);
+ gl.scissor(scissor.i, scissor.j, scissor.w, scissor.h);
+ for (let p = 0; p < this.painters.length; p++) {
+ this.painters[p](gl, viewport);
}
- // Notify
- if ( viewportChanged ) {
- this._viewportChanged.fire( );
- }
- }
-
- paint( gl : WebGLRenderingContext ) {
- var viewport = this._viewport.unmod;
- var scissor = this._scissor.unmod;
- if ( scissor.w > 0 && scissor.h > 0 ) {
- // This pane
- gl.viewport( viewport.i, viewport.j, viewport.w, viewport.h );
- gl.enable( GL.SCISSOR_TEST );
- gl.scissor( scissor.i, scissor.j, scissor.w, scissor.h );
- for ( var p = 0; p < this.painters.length; p++ ) {
- this.painters[ p ]( gl, viewport );
- }
- // Child panes
- for ( var c = 0; c < this.children.length; c++ ) {
- this.children.valueAt( c ).pane.paint( gl );
- }
+ // Child panes
+ for (let c = 0; c < this.children.length; c++) {
+ this.children.valueAt(c).pane.paint(gl);
}
}
+ }
- get viewport( ) : BoundsUnmodifiable {
- return this._viewport.unmod;
- }
+ get viewport(): BoundsUnmodifiable {
+ return this._viewport.unmod;
+ }
- get scissor( ) : BoundsUnmodifiable {
- return this._scissor.unmod;
- }
+ get scissor(): BoundsUnmodifiable {
+ return this._scissor.unmod;
+ }
- get viewportChanged( ) : Notification {
- return this._viewportChanged;
- }
+ get viewportChanged(): Notification {
+ return this._viewportChanged;
+ }
- panesAt( i : number, j : number ) : Pane[] {
- var result : Pane[] = [ ];
- this._panesAt( i, j, result );
- return result;
- }
+ panesAt(i: number, j: number): Pane[] {
+ const result: Pane[] = [];
+ this._panesAt(i, j, result);
+ return result;
+ }
- private _panesAt( i : number, j : number, result : Pane[] ) : boolean {
- if ( this._scissor.contains( i, j ) ) {
- for ( var c = this.children.length-1; c >= 0; c-- ) {
- var consumed = this.children.valueAt( c ).pane._panesAt( i, j, result );
- if ( consumed ) return true;
- }
- if ( this.isInside( this._viewport.unmod, i, j ) ) {
- result.push( this );
- return this.consumesInputEvents;
+ private _panesAt(i: number, j: number, result: Pane[]): boolean {
+ if (this._scissor.contains(i, j)) {
+ for (let c = this.children.length - 1; c >= 0; c--) {
+ const consumed = this.children.valueAt(c).pane._panesAt(i, j, result);
+ if (consumed) {
+ return true;
}
}
- return false;
+ if (this.isInside(this._viewport.unmod, i, j)) {
+ result.push(this);
+ return this.consumesInputEvents;
+ }
}
+ return false;
+ }
+ get mouseUp(): Notification1 { return this._mouseUp; }
+ get mouseDown(): Notification1 { return this._mouseDown; }
+ get mouseMove(): Notification1 { return this._mouseMove; }
+ get mouseWheel(): Notification1 { return this._mouseWheel; }
+ get mouseEnter(): Notification1 { return this._mouseEnter; }
+ get mouseExit(): Notification1 { return this._mouseExit; }
+ get contextMenu(): Notification1 { return this._contextMenu; }
- // Input Handling
- //
-
- private _mouseUp : Notification1 = new Notification1( );
- private _mouseDown : Notification1 = new Notification1( );
- private _mouseMove : Notification1 = new Notification1( );
- private _mouseWheel : Notification1 = new Notification1( );
- private _mouseEnter : Notification1 = new Notification1( );
- private _mouseExit : Notification1 = new Notification1( );
- private _contextMenu : Notification1 = new Notification1( );
+ fireMouseUp(i: number, j: number, clickCount: number, mouseEvent: MouseEvent): any {
+ return this._mouseUp.fire({ paneViewport: this._viewport.unmod, i: i, j: j, clickCount: clickCount, mouseEvent: mouseEvent });
+ }
- get mouseUp( ) : Notification1 { return this._mouseUp; }
- get mouseDown( ) : Notification1 { return this._mouseDown; }
- get mouseMove( ) : Notification1 { return this._mouseMove; }
- get mouseWheel( ) : Notification1 { return this._mouseWheel; }
- get mouseEnter( ) : Notification1 { return this._mouseEnter; }
- get mouseExit( ) : Notification1 { return this._mouseExit; }
- get contextMenu( ) : Notification1 { return this._contextMenu; }
+ fireMouseDown(i: number, j: number, clickCount: number, mouseEvent: MouseEvent): any {
+ return this._mouseDown.fire({ paneViewport: this._viewport.unmod, i: i, j: j, clickCount: clickCount, mouseEvent: mouseEvent });
+ }
- fireMouseUp( i : number, j : number, clickCount : number, mouseEvent : MouseEvent ) : any {
- return this._mouseUp.fire( { paneViewport: this._viewport.unmod, i: i, j: j, clickCount: clickCount, mouseEvent: mouseEvent } );
- }
+ fireMouseMove(i: number, j: number, mouseEvent: MouseEvent): any {
+ return this._mouseMove.fire({ paneViewport: this._viewport.unmod, i: i, j: j, mouseEvent: mouseEvent });
+ }
- fireMouseDown( i : number, j : number, clickCount : number, mouseEvent : MouseEvent ) : any {
- return this._mouseDown.fire( { paneViewport: this._viewport.unmod, i: i, j: j, clickCount: clickCount, mouseEvent: mouseEvent } );
- }
+ fireMouseWheel(i: number, j: number, wheelSteps: number, mouseEvent: MouseEvent): any {
+ return this._mouseWheel.fire({ paneViewport: this._viewport.unmod, i: i, j: j, wheelSteps: wheelSteps, mouseEvent: mouseEvent });
+ }
- fireMouseMove( i : number, j : number, mouseEvent : MouseEvent ) : any {
- return this._mouseMove.fire( { paneViewport: this._viewport.unmod, i: i, j: j, mouseEvent: mouseEvent } );
- }
+ fireMouseEnter(i: number, j: number, mouseEvent: MouseEvent): any {
+ return this._mouseEnter.fire({ paneViewport: this._viewport.unmod, i: i, j: j, mouseEvent: mouseEvent });
+ }
- fireMouseWheel( i : number, j : number, wheelSteps : number, mouseEvent : MouseEvent ) : any {
- return this._mouseWheel.fire( { paneViewport: this._viewport.unmod, i: i, j: j, wheelSteps: wheelSteps, mouseEvent: mouseEvent } );
- }
+ fireMouseExit(i: number, j: number, mouseEvent: MouseEvent): any {
+ return this._mouseExit.fire({ paneViewport: this._viewport.unmod, i: i, j: j, mouseEvent: mouseEvent });
+ }
- fireMouseEnter( i : number, j : number, mouseEvent : MouseEvent ) : any {
- return this._mouseEnter.fire( { paneViewport: this._viewport.unmod, i: i, j: j, mouseEvent: mouseEvent } );
- }
+ fireContextMenu(i: number, j: number, mouseEvent: MouseEvent): any {
+ return this._contextMenu.fire({ paneViewport: this._viewport.unmod, i: i, j: j, mouseEvent: mouseEvent });
+ }
- fireMouseExit( i : number, j : number, mouseEvent : MouseEvent ) : any {
- return this._mouseExit.fire( { paneViewport: this._viewport.unmod, i: i, j: j, mouseEvent: mouseEvent } );
- }
+ // Disposal
+ //
- fireContextMenu( i : number, j : number, mouseEvent : MouseEvent ) : any {
- return this._contextMenu.fire( { paneViewport: this._viewport.unmod, i: i, j: j, mouseEvent: mouseEvent } );
- }
-
- // Disposal
- //
-
- get dispose( ) : Notification {
- return this._dispose;
- }
+ get dispose(): Notification {
+ return this._dispose;
}
+}
- export function requireGL( canvasElement : HTMLCanvasElement ) : WebGLRenderingContext {
- // Wrapping the getContext calls in try-catch blocks may lose information. However,
- // there are two reasons to do so:
- //
- // 1. We want to try 'experimental-webgl' even if 'webgl' throws an error
- // 2. Throwing a custom error should make it easy to show a helpful message
- //
- // Reason 2 is particularly important on Firefox, where the error thrown by getContext
- // has a completely uninformative message.
- //
+export function requireGL(canvasElement: HTMLCanvasElement): WebGLRenderingContext {
+ // Wrapping the getContext calls in try-catch blocks may lose information. However,
+ // there are two reasons to do so:
+ //
+ // 1. We want to try 'experimental-webgl' even if 'webgl' throws an error
+ // 2. Throwing a custom error should make it easy to show a helpful message
+ //
+ // Reason 2 is particularly important on Firefox, where the error thrown by getContext
+ // has a completely uninformative message.
+ //
- try {
- var glA = canvasElement.getContext( 'webgl' );
- if ( glA ) return glA;
+ try {
+ const glA = canvasElement.getContext('webgl');
+ if (glA) {
+ return glA;
}
- catch ( e ) { }
+ }
+ catch (e) { }
- try {
- var glB = canvasElement.getContext( 'experimental-webgl' );
- if ( glB ) return glB;
+ try {
+ const glB = canvasElement.getContext('experimental-webgl') as WebGLRenderingContext;
+ if (glB) {
+ return glB;
}
- catch ( e ) { }
-
- throw new Error( 'WebGLContextCreationError' );
}
+ catch (e) { }
+ throw new Error('WebGLContextCreationError');
+}
- function iMouse( element : HTMLElement, ev : MouseEvent ) : number {
- return ( ev.clientX - element.getBoundingClientRect( ).left );
- }
+function iMouse(element: HTMLElement, ev: MouseEvent): number {
+ return (ev.clientX - element.getBoundingClientRect().left);
+}
- function jMouse( element : HTMLElement, ev : MouseEvent ) : number {
- return ( element.clientHeight - ( ev.clientY - element.getBoundingClientRect( ).top ) );
- }
+function jMouse(element: HTMLElement, ev: MouseEvent): number {
+ return (element.clientHeight - (ev.clientY - element.getBoundingClientRect().top));
+}
- function mouseWheelSteps( ev : MouseWheelEvent ) : number {
- // Firefox puts the scroll amount in the 'detail' field; everybody else puts it in 'wheelDelta'
- // Firefox uses positive values for a downward scroll; everybody else uses positive for upward
- var raw = ( ev.wheelDelta !== undefined ? ev.wheelDelta : -ev.detail );
- if ( raw > 0 ) { return -1; }
- if ( raw < 0 ) { return +1; }
- return 0;
- }
- // Button presses for mouse events are reported differently in different browsers:
- // The results below are for the following browser versions:
- // Chrome Version 40.0.2214.94 (64-bit)
- // Firefox 35.0.1
- // IE 11.0.9600.17501
- //
- // ‘mousemove’ event with left mouse button down:
- //
- // Chrome Firefox IE
- // MouseEvent.button 0 0 0
- // MouseEvent.buttons 1 1 1
- // MouseEvent.which 1 1 1
- //
- // ‘mousemove’ event with no mouse button down:
- //
- // Chrome Firefox IE
- // MouseEvent.button 0 0 0
- // MouseEvent.buttons undefined 0 0
- // MouseEvent.which 0 1 1
- //
- //
- // For more info see: http://stackoverflow.com/questions/3944122/detect-left-mouse-button-press
- //
- export function isLeftMouseDown( ev : MouseEvent ) {
- // it appears that ev.buttons works across the board now, so ev.buttons === 1 is probably all that is necessary
- if ( ev.buttons !== undefined ) {
- return ev.buttons === 1;
- }
- else {
- return ev.which === 1;
- }
+function mouseWheelSteps(ev: WheelEvent): number {
+ // MouseWheelEvent.wheelDelta is deprecated, use WheelEvent.deltaY instead.
+ // Firefox uses positive values for a downward scroll; everybody else uses positive for upward
+ const raw = (ev.deltaY !== undefined ? ev.deltaY : -ev.detail);
+ if (raw > 0) { return -1; }
+ if (raw < 0) { return +1; }
+ return 0;
+}
+
+// Button presses for mouse events are reported differently in different browsers:
+// The results below are for the following browser versions:
+// Chrome Version 40.0.2214.94 (64-bit)
+// Firefox 35.0.1
+// IE 11.0.9600.17501
+//
+// ‘mousemove’ event with left mouse button down:
+//
+// Chrome Firefox IE
+// MouseEvent.button 0 0 0
+// MouseEvent.buttons 1 1 1
+// MouseEvent.which 1 1 1
+//
+// ‘mousemove’ event with no mouse button down:
+//
+// Chrome Firefox IE
+// MouseEvent.button 0 0 0
+// MouseEvent.buttons undefined 0 0
+// MouseEvent.which 0 1 1
+//
+//
+// For more info see: http://stackoverflow.com/questions/3944122/detect-left-mouse-button-press
+//
+export function isLeftMouseDown(ev: MouseEvent) {
+ // it appears that ev.buttons works across the board now, so ev.buttons === 1 is probably all that is necessary
+ if (ev.buttons !== undefined) {
+ return ev.buttons === 1;
}
-
- // detects whether any mouse button is down
- export function isMouseDown( ev : MouseEvent ) {
- return ev.buttons !== 0;
+ else {
+ return ev.which === 1;
}
+}
- function attachEventListeners( element : HTMLElement, contentPane : Pane ) {
+// detects whether any mouse button is down
+export function isMouseDown(ev: MouseEvent) {
+ return ev.buttons !== 0;
+}
- function detectEntersAndExits( oldPanes : Pane[], newPanes : Pane[], i : number, j : number, mouseEvent : MouseEvent ) {
- for ( var n = 0; n < oldPanes.length; n++ ) {
- oldPanes[ n ][ 'isHovered' ] = false;
- }
- for ( var n = 0; n < newPanes.length; n++ ) {
- newPanes[ n ][ 'isHovered' ] = true;
- }
- for ( var n = 0; n < oldPanes.length; n++ ) {
- var oldPane = oldPanes[ n ];
- if ( !oldPane[ 'isHovered' ] ) {
- oldPane.fireMouseExit( i, j, mouseEvent );
- }
- }
+function attachEventListeners(element: HTMLElement, contentPane: Pane) {
- for ( var n = 0; n < newPanes.length; n++ ) {
- newPanes[ n ][ 'wasHovered' ] = false;
+ function detectEntersAndExits(oldPanes: Pane[], newPanes: Pane[], i: number, j: number, mouseEvent: MouseEvent) {
+ for (let n = 0; n < oldPanes.length; n++) {
+ oldPanes[n]['isHovered'] = false;
+ }
+ for (let n = 0; n < newPanes.length; n++) {
+ newPanes[n]['isHovered'] = true;
+ }
+ for (let n = 0; n < oldPanes.length; n++) {
+ const oldPane = oldPanes[n];
+ if (!oldPane['isHovered']) {
+ oldPane.fireMouseExit(i, j, mouseEvent);
}
- for ( var n = 0; n < oldPanes.length; n++ ) {
- oldPanes[ n ][ 'wasHovered' ] = true;
+ }
+
+ for (let n = 0; n < newPanes.length; n++) {
+ newPanes[n]['wasHovered'] = false;
+ }
+ for (let n = 0; n < oldPanes.length; n++) {
+ oldPanes[n]['wasHovered'] = true;
+ }
+ for (let n = 0; n < newPanes.length; n++) {
+ const newPane = newPanes[n];
+ if (!newPane['wasHovered']) {
+ newPane.fireMouseEnter(i, j, mouseEvent);
}
- for ( var n = 0; n < newPanes.length; n++ ) {
- var newPane = newPanes[ n ];
- if ( !newPane[ 'wasHovered' ] ) {
- newPane.fireMouseEnter( i, j, mouseEvent );
- }
+ }
+ }
+
+ // Support for reporting click count. Browsers handle reporting single/double
+ // clicks differently, so it's generally not a good idea to use both listeners
+ // at one. That's why it's done this way instead of using the 'dblclick' event.
+ const multiPressTimeout_MILLIS = 250;
+ let prevPress_PMILLIS = 0;
+ let clickCount = 1;
+
+ let currentPanes: Pane[] = [];
+ let currentMouseCursor: string = null;
+ let dragging = false;
+ let pendingExit = false;
+
+ function refreshMouseCursor() {
+ let newMouseCursor = null;
+ for (let n = 0; n < currentPanes.length; n++) {
+ newMouseCursor = currentPanes[n].mouseCursor;
+ if (hasval(newMouseCursor)) {
+ break;
}
}
+ if (!hasval(newMouseCursor)) {
+ newMouseCursor = 'default';
+ }
+ if (newMouseCursor !== currentMouseCursor) {
+ element.style.cursor = newMouseCursor;
+ currentMouseCursor = newMouseCursor;
+ }
+ }
+ refreshMouseCursor();
+ contentPane.mouseCursorChanged.on(refreshMouseCursor);
- // Support for reporting click count. Browsers handle reporting single/double
- // clicks differently, so it's generally not a good idea to use both listeners
- // at one. That's why it's done this way instead of using the 'dblclick' event.
- var multiPressTimeout_MILLIS = 250;
- var prevPress_PMILLIS = 0;
- var clickCount = 1;
- var currentPanes : Pane[] = [];
- var currentMouseCursor : string = null;
- var dragging : boolean = false;
- var pendingExit : boolean = false;
+ element.addEventListener('mousedown', function (ev: MouseEvent) {
+ const press_PMILLIS = (new Date()).getTime();
+ const i = iMouse(element, ev);
+ const j = jMouse(element, ev);
- function refreshMouseCursor( ) {
- var newMouseCursor = null;
- for ( var n = 0; n < currentPanes.length; n++ ) {
- newMouseCursor = currentPanes[ n ].mouseCursor;
- if ( hasval( newMouseCursor ) ) break;
- }
- if ( !hasval( newMouseCursor ) ) {
- newMouseCursor = 'default';
- }
- if ( newMouseCursor !== currentMouseCursor ) {
- element.style.cursor = newMouseCursor;
- currentMouseCursor = newMouseCursor;
- }
+ if (press_PMILLIS - prevPress_PMILLIS < multiPressTimeout_MILLIS) {
+ clickCount++;
}
- refreshMouseCursor( );
- contentPane.mouseCursorChanged.on( refreshMouseCursor );
+ else {
+ clickCount = 1;
+ }
+ prevPress_PMILLIS = press_PMILLIS;
+ const newPanes = contentPane.panesAt(i, j);
+ detectEntersAndExits(currentPanes, newPanes, i, j, ev);
+ currentPanes = newPanes;
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseDown(i, j, clickCount, ev);
+ }
+ refreshMouseCursor();
- element.addEventListener( 'mousedown', function( ev : MouseEvent ) {
- var press_PMILLIS = ( new Date( ) ).getTime( );
- var i = iMouse( element, ev );
- var j = jMouse( element, ev );
+ dragging = true;
- if ( press_PMILLIS - prevPress_PMILLIS < multiPressTimeout_MILLIS ) {
- clickCount++;
- }
- else {
- clickCount = 1;
- }
- prevPress_PMILLIS = press_PMILLIS;
+ // Disable browser-default double-click action, which selects text and messes up subsequent drags
+ ev.preventDefault();
+ });
+
+ // Only want NON-DRAG moves from the canvas (e.g. we don't want moves that occur in an overlay div) -- so subscribe to CANVAS's mousemove
+ element.addEventListener('mousemove', function (ev: MouseEvent) {
+ if (!dragging) {
+ const i = iMouse(element, ev);
+ const j = jMouse(element, ev);
- var newPanes = contentPane.panesAt( i, j );
- detectEntersAndExits( currentPanes, newPanes, i, j, ev );
+ const newPanes = contentPane.panesAt(i, j);
+ detectEntersAndExits(currentPanes, newPanes, i, j, ev);
currentPanes = newPanes;
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseDown( i, j, clickCount, ev );
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseMove(i, j, ev);
}
- refreshMouseCursor( );
+ refreshMouseCursor();
+ }
+ });
- dragging = true;
+ // During a DRAG we want all move events, even ones that occur outside the canvas -- so subscribe to WINDOW's mousemove
+ window.addEventListener('mousemove', function (ev: MouseEvent) {
+ if (dragging) {
+ const i = iMouse(element, ev);
+ const j = jMouse(element, ev);
- // Disable browser-default double-click action, which selects text and messes up subsequent drags
- ev.preventDefault( );
- } );
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseMove(i, j, ev);
+ }
+ }
+ });
- // Only want NON-DRAG moves from the canvas (e.g. we don't want moves that occur in an overlay div) -- so subscribe to CANVAS's mousemove
- element.addEventListener( 'mousemove', function( ev : MouseEvent ) {
- if ( !dragging ) {
- var i = iMouse( element, ev );
- var j = jMouse( element, ev );
+ element.addEventListener('mouseout', function (ev: MouseEvent) {
+ const i = iMouse(element, ev);
+ const j = jMouse(element, ev);
- var newPanes = contentPane.panesAt( i, j );
- detectEntersAndExits( currentPanes, newPanes, i, j, ev );
- currentPanes = newPanes;
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseMove( i, j, ev );
- }
- refreshMouseCursor( );
+ if (dragging) {
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseMove(i, j, ev);
}
- } );
+ pendingExit = true;
+ }
+ else {
+ detectEntersAndExits(currentPanes, [], i, j, ev);
+ currentPanes = [];
+ refreshMouseCursor();
+ }
+ });
- // During a DRAG we want all move events, even ones that occur outside the canvas -- so subscribe to WINDOW's mousemove
- window.addEventListener( 'mousemove', function( ev : MouseEvent ) {
- if ( dragging ) {
- var i = iMouse( element, ev );
- var j = jMouse( element, ev );
+ element.addEventListener('mouseover', function (ev: MouseEvent) {
+ const i = iMouse(element, ev);
+ const j = jMouse(element, ev);
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseMove( i, j, ev );
- }
+ if (dragging) {
+ pendingExit = false;
+ }
+ else {
+ const newPanes = contentPane.panesAt(i, j);
+ detectEntersAndExits(currentPanes, newPanes, i, j, ev);
+ currentPanes = newPanes;
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseMove(i, j, ev);
}
- } );
+ refreshMouseCursor();
+ }
+ });
- element.addEventListener( 'mouseout', function( ev : MouseEvent ) {
- var i = iMouse( element, ev );
- var j = jMouse( element, ev );
+ const endDrag = function (ev: MouseEvent) {
+ const i = iMouse(element, ev);
+ const j = jMouse(element, ev);
- if ( dragging ) {
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseMove( i, j, ev );
- }
- pendingExit = true;
- }
- else {
- detectEntersAndExits( currentPanes, [], i, j, ev );
- currentPanes = [];
- refreshMouseCursor( );
- }
- } );
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseUp(i, j, clickCount, ev);
+ }
- element.addEventListener( 'mouseover', function( ev : MouseEvent ) {
- var i = iMouse( element, ev );
- var j = jMouse( element, ev );
+ dragging = false;
- if ( dragging ) {
- pendingExit = false;
- }
- else {
- var newPanes = contentPane.panesAt( i, j );
- detectEntersAndExits( currentPanes, newPanes, i, j, ev );
- currentPanes = newPanes;
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseMove( i, j, ev );
- }
- refreshMouseCursor( );
- }
- } );
-
- var endDrag = function( ev : MouseEvent ) {
- var i = iMouse( element, ev );
- var j = jMouse( element, ev );
-
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseUp( i, j, clickCount, ev );
- }
-
- dragging = false;
-
- if ( pendingExit ) {
- detectEntersAndExits( currentPanes, [], i, j, ev );
- currentPanes = [];
- pendingExit = false;
- refreshMouseCursor( );
- }
- else {
- var newPanes = contentPane.panesAt( i, j );
- detectEntersAndExits( currentPanes, newPanes, i, j, ev );
- currentPanes = newPanes;
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseMove( i, j, ev );
- }
- refreshMouseCursor( );
+ if (pendingExit) {
+ detectEntersAndExits(currentPanes, [], i, j, ev);
+ currentPanes = [];
+ pendingExit = false;
+ refreshMouseCursor();
+ }
+ else {
+ const newPanes = contentPane.panesAt(i, j);
+ detectEntersAndExits(currentPanes, newPanes, i, j, ev);
+ currentPanes = newPanes;
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseMove(i, j, ev);
}
- };
+ refreshMouseCursor();
+ }
+ };
+
+ // The window always gets the mouse-up event at the end of a drag -- even if it occurs outside the browser window
+ window.addEventListener('mouseup', function (ev: MouseEvent) {
+ if (dragging) {
+ endDrag(ev);
+ }
+ });
+
+ // We don't receive mouse events that happen over another iframe -- even during a drag. If we miss a mouseup that
+ // should terminate a drag, we end up stuck in dragging mode, which makes for a really lousy user experience. To
+ // avoid that, whenever the mouse moves, check whether the button is down. If we're still in dragging mode, but
+ // the button is now up, end the drag.
- // The window always gets the mouse-up event at the end of a drag -- even if it occurs outside the browser window
- window.addEventListener( 'mouseup', function( ev : MouseEvent ) {
- if ( dragging ) {
- endDrag( ev );
+ // If we're dragging, and we see a mousemove with no buttons down, end the drag
+ let recentDrag: MouseEvent = null;
+ const handleMissedMouseUp = function (ev: MouseEvent) {
+ if (dragging) {
+ if (!isMouseDown(ev) && recentDrag) {
+ const mouseUp = document.createEvent('MouseEvents');
+ mouseUp.initMouseEvent('mouseup', true, true, window, 0, recentDrag.screenX, recentDrag.screenY, ev.screenX - window.screenX, ev.screenY - window.screenY, recentDrag.ctrlKey, recentDrag.altKey, recentDrag.shiftKey, recentDrag.metaKey, 0, null);
+ endDrag(mouseUp);
}
- } );
-
- // We don't receive mouse events that happen over another iframe -- even during a drag. If we miss a mouseup that
- // should terminate a drag, we end up stuck in dragging mode, which makes for a really lousy user experience. To
- // avoid that, whenever the mouse moves, check whether the button is down. If we're still in dragging mode, but
- // the button is now up, end the drag.
-
- // If we're dragging, and we see a mousemove with no buttons down, end the drag
- var recentDrag : MouseEvent = null;
- var handleMissedMouseUp = function( ev : MouseEvent ) {
- if ( dragging ) {
- if ( !isMouseDown( ev ) && recentDrag ) {
- var mouseUp = document.createEvent( 'MouseEvents' );
- mouseUp.initMouseEvent( 'mouseup', true, true, window, 0, recentDrag.screenX, recentDrag.screenY, ev.screenX - window.screenX, ev.screenY - window.screenY, recentDrag.ctrlKey, recentDrag.altKey, recentDrag.shiftKey, recentDrag.metaKey, 0, null );
- endDrag( mouseUp );
+ recentDrag = ev;
+ }
+ };
+ window.addEventListener('mousemove', handleMissedMouseUp);
+ let w = window;
+ while (w.parent !== w) {
+ try {
+ w.parent.addEventListener('mousemove', handleMissedMouseUp);
+ w = w.parent as Window & typeof globalThis;
+ }
+ catch (e) {
+ // Cross-origin security may prevent us from adding a listener to a window other than our own -- in that case,
+ // the least bad option is to terminate drags on exit from the highest accessible window
+ w.addEventListener('mouseout', function (ev: MouseEvent) {
+ if (dragging) {
+ const mouseUp = document.createEvent('MouseEvents');
+ mouseUp.initMouseEvent('mouseup', true, true, window, 0, ev.screenX, ev.screenY, ev.screenX - window.screenX, ev.screenY - window.screenY, ev.ctrlKey, ev.altKey, ev.shiftKey, ev.metaKey, 0, null);
+ endDrag(mouseUp);
}
- recentDrag = ev;
- }
- };
- window.addEventListener( 'mousemove', handleMissedMouseUp );
- var w = window;
- while ( w.parent !== w ) {
- try {
- w.parent.addEventListener( 'mousemove', handleMissedMouseUp );
- w = w.parent;
- }
- catch ( e ) {
- // Cross-origin security may prevent us from adding a listener to a window other than our own -- in that case,
- // the least bad option is to terminate drags on exit from the highest accessible window
- w.addEventListener( 'mouseout', function( ev : MouseEvent ) {
- if ( dragging ) {
- var mouseUp = document.createEvent( 'MouseEvents' );
- mouseUp.initMouseEvent( 'mouseup', true, true, window, 0, ev.screenX, ev.screenY, ev.screenX - window.screenX, ev.screenY - window.screenY, ev.ctrlKey, ev.altKey, ev.shiftKey, ev.metaKey, 0, null );
- endDrag( mouseUp );
- }
- } );
- break;
- }
+ });
+ break;
}
+ }
- // Firefox uses event type 'DOMMouseScroll' for mouse-wheel events; everybody else uses 'mousewheel'
- var handleMouseWheel = function( ev : MouseWheelEvent ) {
- var i = iMouse( element, ev );
- var j = jMouse( element, ev );
+ const handleMouseWheel = function (ev: WheelEvent) {
+ const i = iMouse(element, ev);
+ const j = jMouse(element, ev);
- if ( dragging ) {
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseMove( i, j, ev );
- }
+ if (dragging) {
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseMove(i, j, ev);
}
- else {
- var newPanes = contentPane.panesAt( i, j );
- detectEntersAndExits( currentPanes, newPanes, i, j, ev );
- currentPanes = newPanes;
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseMove( i, j, ev );
- }
+ }
+ else {
+ const newPanes = contentPane.panesAt(i, j);
+ detectEntersAndExits(currentPanes, newPanes, i, j, ev);
+ currentPanes = newPanes;
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseMove(i, j, ev);
}
+ }
- var wheelSteps = mouseWheelSteps( ev );
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseWheel( i, j, wheelSteps, ev );
- }
- };
- element.addEventListener( 'mousewheel', handleMouseWheel );
- element.addEventListener( 'DOMMouseScroll', handleMouseWheel, false );
+ const wheelSteps = mouseWheelSteps(ev);
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseWheel(i, j, wheelSteps, ev);
+ }
+ };
+ element.addEventListener('wheel', handleMouseWheel);
- element.addEventListener( 'contextmenu', function( ev : MouseEvent ) {
- var i = iMouse( element, ev );
- var j = jMouse( element, ev );
+ element.addEventListener('contextmenu', function (ev: MouseEvent) {
+ const i = iMouse(element, ev);
+ const j = jMouse(element, ev);
- if ( dragging ) {
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseMove( i, j, ev );
- }
+ if (dragging) {
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseMove(i, j, ev);
}
- else {
- var newPanes = contentPane.panesAt( i, j );
- detectEntersAndExits( currentPanes, newPanes, i, j, ev );
- currentPanes = newPanes;
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireMouseMove( i, j, ev );
- }
- refreshMouseCursor( );
+ }
+ else {
+ const newPanes = contentPane.panesAt(i, j);
+ detectEntersAndExits(currentPanes, newPanes, i, j, ev);
+ currentPanes = newPanes;
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireMouseMove(i, j, ev);
}
+ refreshMouseCursor();
+ }
- for ( var n = 0; n < currentPanes.length; n++ ) {
- currentPanes[ n ].fireContextMenu( i, j, ev );
- }
+ for (let n = 0; n < currentPanes.length; n++) {
+ currentPanes[n].fireContextMenu(i, j, ev);
+ }
- // Disable browser-default context menu
- ev.preventDefault( );
- } );
- }
+ // Disable browser-default context menu
+ ev.preventDefault();
+ });
+}
- export interface Drawable {
- setContentPane( pane : Pane );
- redraw( );
-
- getPrefSize( ) : Size;
- prefSizeChanged( ) : Notification1;
- }
+export interface Drawable {
+ setContentPane(pane: Pane): void;
+ redraw(): void;
+ getPrefSize(): Size;
+ prefSizeChanged(): Notification1;
+}
- export function newDrawable( canvas : HTMLCanvasElement ) : Drawable {
- var contentPane : Pane = null;
- var contentPrefSizeNotification = new Notification1( );
- var contentPrefSize = { w: null, h: null };
- var contentViewport = newBoundsFromRect( 0, 0, 0, 0 );
- var gl = requireGL( canvas );
- var pendingRequestId = null;
- return {
- setContentPane: function( pane : Pane ) {
- if ( hasval( contentPane ) ) {
- // XXX: Detach listeners from old contentPane
- }
- contentPane = pane;
- attachEventListeners( canvas, contentPane );
- },
- redraw: function( ) {
- if ( !hasval( pendingRequestId ) ) {
- pendingRequestId = window.requestAnimationFrame( function( ) {
- if ( hasval( contentPane ) ) {
-
- var oldPrefSize = { w: contentPrefSize.w, h: contentPrefSize.h };
-
- contentPane.updatePrefSizes( contentPrefSize );
- contentViewport.setRect( 0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight );
- contentPane.updateBounds( contentViewport.unmod, contentViewport.unmod );
- contentPane.paint( gl );
-
- // XXX: Trigger an enter/exit check somehow (fake a mouse-event?)
- }
- pendingRequestId = null;
-
- if ( oldPrefSize.w !== contentPrefSize.w || oldPrefSize.h !== contentPrefSize.h ) {
- contentPrefSizeNotification.fire ( { w: contentPrefSize.w, h: contentPrefSize.h } );
- }
- } );
- }
- },
- getPrefSize: function( ) {
- return { w: contentPrefSize.w, h: contentPrefSize.h };
- },
- prefSizeChanged: function( ) : Notification1 {
- return contentPrefSizeNotification;
- }
- };
- }
+export function newDrawable(canvas: HTMLCanvasElement): Drawable {
+ let contentPane: Pane = null;
+ const contentPrefSizeNotification = new Notification1();
+ const contentPrefSize = { w: null, h: null };
+ const contentViewport = newBoundsFromRect(0, 0, 0, 0);
+ const gl = requireGL(canvas);
+ let pendingRequestId = null;
+ return {
+ setContentPane: function (pane: Pane) {
+ if (hasval(contentPane)) {
+ // XXX: Detach listeners from old contentPane
+ }
+ contentPane = pane;
+ attachEventListeners(canvas, contentPane);
+ },
+ redraw: function () {
+ if (!hasval(pendingRequestId)) {
+ pendingRequestId = window.requestAnimationFrame(function () {
+ let oldPrefSize = { w: null, h: null };
+ if (hasval(contentPane)) {
+
+ oldPrefSize = { w: contentPrefSize.w, h: contentPrefSize.h };
+
+ contentPane.updatePrefSizes(contentPrefSize);
+ contentViewport.setRect(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
+ contentPane.updateBounds(contentViewport.unmod, contentViewport.unmod);
+ contentPane.paint(gl);
+
+ // XXX: Trigger an enter/exit check somehow (fake a mouse-event?)
+ }
+ pendingRequestId = null;
+ if (oldPrefSize.w !== contentPrefSize.w || oldPrefSize.h !== contentPrefSize.h) {
+ contentPrefSizeNotification.fire({ w: contentPrefSize.w, h: contentPrefSize.h });
+ }
+ });
+ }
+ },
+ getPrefSize: function () {
+ return { w: contentPrefSize.w, h: contentPrefSize.h };
+ },
+ prefSizeChanged: function (): Notification1 {
+ return contentPrefSizeNotification;
+ }
+ };
}
diff --git a/src/webglimpse/defs/moment.d.ts b/src/webglimpse/defs/moment.d.ts
deleted file mode 100644
index f913ab6..0000000
--- a/src/webglimpse/defs/moment.d.ts
+++ /dev/null
@@ -1,327 +0,0 @@
-// Type definitions for Moment.js 2.5.0
-// Project: https://github.com/timrwood/moment
-// Definitions by: Michael Lakerveld
-// 2.4.0 Aaron King
-// 2.5.0 Hiroki Horiuchi
-// DefinitelyTyped: https://github.com/borisyankov/DefinitelyTyped
-
-
-interface MomentInput {
- years?: number;
- y?: number;
- months?: number;
- M?: number;
- weeks?: number;
- w?: number;
- days?: number;
- d?: number;
- hours?: number;
- h?: number;
- minutes?: number;
- m?: number;
- seconds?: number;
- s?: number;
- milliseconds?: number;
- ms?: number;
-}
-
-
-interface Duration {
-
- humanize(): string;
-
- milliseconds(): number;
- asMilliseconds(): number;
-
- seconds(): number;
- asSeconds(): number;
-
- minutes(): number;
- asMinutes(): number;
-
- hours(): number;
- asHours(): number;
-
- days(): number;
- asDays(): number;
-
- months(): number;
- asMonths(): number;
-
- years(): number;
- asYears(): number;
-
-}
-
-interface Moment {
-
- format(format: string): string;
- format(): string;
-
- fromNow(withoutSuffix?: boolean): string;
-
- startOf(soort: string): Moment;
- endOf(soort: string): Moment;
-
- add(input: MomentInput): Moment;
- add(soort: string, aantal: number): Moment;
- add(duration: Duration): Moment;
- subtract(input: MomentInput): Moment;
- subtract(soort: string, aantal: number): Moment;
-
- calendar(): string;
- clone(): Moment;
-
- valueOf(): number;
-
- local(): Moment; // current date/time in local mode
-
- utc(): Moment; // current date/time in UTC mode
-
- isValid(): boolean;
-
- year(y: number): Moment;
- year(): number;
- quarter(): number;
- month(M: number): Moment;
- month(M: string): Moment;
- month(): number;
- day(d: number): Moment;
- day(d: string): Moment;
- day(): number;
- date(d: number): Moment;
- date(): number;
- hours(h: number): Moment;
- hours(): number;
- minutes(m: number): Moment;
- minutes(): number;
- seconds(s: number): Moment;
- seconds(): number;
- milliseconds(ms: number): Moment;
- milliseconds(): number;
- weekday(): number;
- weekday(d: number): Moment;
- isoWeekday(): number;
- isoWeekday(d: number): Moment;
- weekYear(): number;
- weekYear(d: number): Moment;
- isoWeekYear(): number;
- isoWeekYear(d: number): Moment;
- week(): number;
- week(d: number): Moment;
- weeks(): number;
- weeks(d: number): Moment;
- isoWeek(): number;
- isoWeek(d: number): Moment;
- isoWeeks(): number;
- isoWeeks(d: number): Moment;
-
- from(f: Moment): string;
- from(f: Moment, suffix: boolean): string;
- from(d: Date): string;
- from(s: string): string;
- from(date: number[]): string;
-
- diff(b: Moment): number;
- diff(b: Moment, soort: string): number;
- diff(b: Moment, soort: string, round: boolean): number;
-
- toDate(): Date;
- toISOString(): string;
- unix(): number;
-
- isLeapYear(): boolean;
- zone(): number;
- zone(b: number): Moment;
- zone(b: string): Moment;
- daysInMonth(): number;
- isDST(): boolean;
-
- isBefore(): boolean;
- isBefore(b: Moment): boolean;
- isBefore(b: string): boolean;
- isBefore(b: Number): boolean;
- isBefore(b: Date): boolean;
- isBefore(b: number[]): boolean;
- isBefore(b: Moment, granularity: string): boolean;
- isBefore(b: String, granularity: string): boolean;
- isBefore(b: Number, granularity: string): boolean;
- isBefore(b: Date, granularity: string): boolean;
- isBefore(b: number[], granularity: string): boolean;
-
- isAfter(): boolean;
- isAfter(b: Moment): boolean;
- isAfter(b: string): boolean;
- isAfter(b: Number): boolean;
- isAfter(b: Date): boolean;
- isAfter(b: number[]): boolean;
- isAfter(b: Moment, granularity: string): boolean;
- isAfter(b: String, granularity: string): boolean;
- isAfter(b: Number, granularity: string): boolean;
- isAfter(b: Date, granularity: string): boolean;
- isAfter(b: number[], granularity: string): boolean;
-
- isSame(b: Moment): boolean;
- isSame(b: string): boolean;
- isSame(b: Number): boolean;
- isSame(b: Date): boolean;
- isSame(b: number[]): boolean;
- isSame(b: Moment, granularity: string): boolean;
- isSame(b: String, granularity: string): boolean;
- isSame(b: Number, granularity: string): boolean;
- isSame(b: Date, granularity: string): boolean;
- isSame(b: number[], granularity: string): boolean;
-
- lang(language: string): void;
- lang(reset: boolean): void;
- lang(): string;
-
- max(date: Date): Moment;
- max(date: number): Moment;
- max(date: any[]): Moment;
- max(date: string): Moment;
- max(date: string, format: string): Moment;
- max(clone: Moment): Moment;
-
- min(date: Date): Moment;
- min(date: number): Moment;
- min(date: any[]): Moment;
- min(date: string): Moment;
- min(date: string, format: string): Moment;
- min(clone: Moment): Moment;
-
- get(unit: string): number;
- set(unit: string, value: number): Moment;
-
-}
-
-interface MomentCalendar {
-
- lastDay: any;
- sameDay: any;
- nextDay: any;
- lastWeek: any;
- nextWeek: any;
- sameElse: any;
-
-}
-
-interface MomentLanguage {
-
- months?: any;
- monthsShort?: any;
- weekdays?: any;
- weekdaysShort?: any;
- weekdaysMin?: any;
- longDateFormat?: MomentLongDateFormat;
- relativeTime?: MomentRelativeTime;
- meridiem?: (hour: number, minute: number, isLowercase: boolean) => string;
- calendar?: MomentCalendar;
- ordinal?: (num: number) => string;
-
-}
-
-interface MomentLongDateFormat {
-
- L: string;
- LL: string;
- LLL: string;
- LLLL: string;
- LT: string;
- l?: string;
- ll?: string;
- lll?: string;
- llll?: string;
- lt?: string;
-
-}
-
-interface MomentRelativeTime {
-
- future: any;
- past: any;
- s: any;
- m: any;
- mm: any;
- h: any;
- hh: any;
- d: any;
- dd: any;
- M: any;
- MM: any;
- y: any;
- yy: any;
-
-}
-
-interface MomentStatic {
-
- (): Moment;
- (date: number): Moment;
- (date: number[]): Moment;
- (date: string, format?: string, strict?: boolean): Moment;
- (date: string, format?: string, language?: string, strict?: boolean): Moment;
- (date: string, formats: string[], strict?: boolean): Moment;
- (date: string, formats: string[], language?: string, strict?: boolean): Moment;
- (date: Date): Moment;
- (date: Moment): Moment;
- (date: Object): Moment;
-
- utc(): Moment;
- utc(date: number): Moment;
- utc(date: number[]): Moment;
- utc(date: string, format?: string, strict?: boolean): Moment;
- utc(date: string, format?: string, language?: string, strict?: boolean): Moment;
- utc(date: string, formats: string[], strict?: boolean): Moment;
- utc(date: string, formats: string[], language?: string, strict?: boolean): Moment;
- utc(date: Date): Moment;
- utc(date: Moment): Moment;
- utc(date: Object): Moment;
-
- unix(timestamp: number): Moment;
-
- isMoment(): boolean;
- isMoment(m: any): boolean;
- lang(language: string): any;
- lang(language: string, definition: MomentLanguage): any;
- longDateFormat: any;
- relativeTime: any;
- meridiem: (hour: number, minute: number, isLowercase: boolean) => string;
- calendar: any;
- ordinal: (num: number) => string;
-
- duration(milliseconds: Number): Duration;
- duration(num: Number, soort: string): Duration;
- duration(input: MomentInput): Duration;
- duration(object: any): Duration;
- duration(): Duration;
-
- parseZone(date: string): Moment;
-
- months(): string[];
- months(index: number): string;
- months(format: string): string[];
- months(format: string, index: number): string;
- monthsShort(): string[];
- monthsShort(index: number): string;
- monthsShort(format: string): string[];
- monthsShort(format: string, index: number): string;
- weekdays(): string[];
- weekdays(index: number): string;
- weekdays(format: string): string[];
- weekdays(format: string, index: number): string;
- weekdaysShort(): string[];
- weekdaysShort(index: number): string;
- weekdaysShort(format: string): string[];
- weekdaysShort(format: string, index: number): string;
- weekdaysMin(): string[];
- weekdaysMin(index: number): string;
- weekdaysMin(format: string): string[];
- weekdaysMin(format: string, index: number): string;
-
- normalizeUnits(unit: string): string;
-
- invalid(parsingFlags?: Object): Moment;
-}
-
-declare var moment: MomentStatic;
diff --git a/src/webglimpse/gradient.ts b/src/webglimpse/gradient.ts
index a7558bf..52794c2 100644
--- a/src/webglimpse/gradient.ts
+++ b/src/webglimpse/gradient.ts
@@ -27,69 +27,63 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Texture, FloatDataTexture2D } from './texture';
- // value: a number from 0-1
- // out: four component RGBA array indicating the gradient color for the input fraction
- export interface Gradient {
- ( value : number ) : number[];
+// value: a number from 0-1
+// out: four component RGBA array indicating the gradient color for the input fraction
+export type Gradient = (value: number) => number[];
+
+// fills an array with jet colorscale values
+// webgl does not support 1D textures, so a 2D texture must be used
+export function jet(value: number): number[] {
+
+ const x = 4.0 * value;
+
+ const r = clamp(1.5 - Math.abs(x - 3.0), 0, 1);
+ const g = clamp(1.5 - Math.abs(x - 2.0), 0, 1);
+ const b = clamp(1.5 - Math.abs(x - 1.0), 0, 1);
+
+ return [r, g, b, 1.0];
+}
+
+export function reverseBone(value: number): number[] {
+
+ const x = 1 - 0.875 * value;
+ if (value < 0.375) {
+ return [x, x, x - value / 3, 1];
+ }
+ else if (value < 0.75) {
+ return [x, x + 0.125 - value / 3, x - 0.125, 1];
}
-
- // fills an array with jet colorscale values
- // webgl does not support 1D textures, so a 2D texture must be used
- export function jet( value : number ) : number[] {
-
- var x = 4.0 * value;
-
- var r = clamp( 1.5 - Math.abs( x - 3.0 ), 0, 1 );
- var g = clamp( 1.5 - Math.abs( x - 2.0 ), 0, 1 );
- var b = clamp( 1.5 - Math.abs( x - 1.0 ), 0, 1 );
-
- return [ r, g, b, 1.0 ];
+ else {
+ return [x + 0.375 - value * 0.5, x - 0.125, x - 0.125, 1];
}
-
- export function reverseBone( value : number ) : number[] {
-
- var x = 1 - 0.875 * value;
- if ( value < 0.375 )
- {
- return [ x, x, x - value / 3, 1 ];
- }
- else if ( value < 0.75 )
- {
- return [ x, x + 0.125 - value / 3, x - 0.125, 1 ];
- }
- else
- {
- return [ x + 0.375 - value * 0.5, x - 0.125, x - 0.125, 1 ];
- }
-
+
+}
+
+export function getGradientTexture(gradient: Gradient, size: number = 1024): Texture {
+ const array = new Float32Array(size * 4);
+
+ for (let v = 0; v < size; v++) {
+ const color = gradient(v / size);
+
+ array[4 * v + 0] = color[0];
+ array[4 * v + 1] = color[1];
+ array[4 * v + 2] = color[2];
+ array[4 * v + 3] = color[3];
+ }
+
+ return new FloatDataTexture2D(size, 1, array);
+}
+
+function clamp(n: number, min: number, max: number): number {
+ if (n < min) {
+ return min;
}
-
- export function getGradientTexture( gradient : Gradient, size : number = 1024 ) : Texture {
- var array = new Float32Array( size * 4 );
-
- for ( var v = 0 ; v < size ; v++ ) {
- var color = gradient( v / size );
-
- array[ 4*v + 0 ] = color[0];
- array[ 4*v + 1 ] = color[1];
- array[ 4*v + 2 ] = color[2];
- array[ 4*v + 3 ] = color[3];
- }
-
- return new FloatDataTexture2D( size, 1, array );
+ else if (n > max) {
+ return max;
}
-
- function clamp( n : number, min : number, max : number ) : number {
- if ( n < min ) {
- return min;
- }
- else if ( n > max ) {
- return max;
- }
- else {
- return n;
- }
+ else {
+ return n;
}
-}
\ No newline at end of file
+}
diff --git a/src/webglimpse/label.ts b/src/webglimpse/label.ts
index 9b3b982..5886e94 100644
--- a/src/webglimpse/label.ts
+++ b/src/webglimpse/label.ts
@@ -27,131 +27,148 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
+import {Color, sameColor} from './color';
+import {TextTextureFactory, TextTexture2D, createTextTextureFactory, getTextWidth} from './text';
+import {Size, BoundsUnmodifiable} from './bounds';
+import {TextureRenderer} from './texture';
+import {GL, hasval} from './util/util';
+import {LayoutPhase1} from './core';
+
+export class Label {
+ private _font: string;
+ private _fgColor: Color;
+ private _bgColor: Color;
+ private _text: string;
+ private _textureFactory: TextTextureFactory;
+ private _texture: TextTexture2D;
+
+ constructor(text?: string, font?: string, fgColor?: Color, bgColor?: Color) {
+ this._font = font;
+ this._text = text;
+ this._fgColor = fgColor;
+ this._bgColor = bgColor;
+ }
- export class Label {
- private _font : string;
- private _fgColor : Color;
- private _bgColor : Color;
- private _text : string;
- private _textureFactory : TextTextureFactory;
- private _texture : TextTexture2D;
+ get font(): string {
+ return this._font;
+ }
- constructor( text? : string, font? : string, fgColor? : Color, bgColor? : Color ) {
+ set font(font: string) {
+ if (this._font !== font) {
this._font = font;
- this._text = text;
- this._fgColor = fgColor;
- this._bgColor = bgColor;
+ this._textureFactory = null;
+ if (this._texture) {
+ this._texture.dispose();
+ this._texture = null;
+ }
}
+ }
- get font( ) : string {
- return this._font;
- }
+ // retained for backwards compatibility, should use fgColor
+ get color(): Color {
+ return this._fgColor;
+ }
- set font( font : string ) {
- if ( this._font !== font ) {
- this._font = font;
- this._textureFactory = null;
- if ( this._texture ) {
- this._texture.dispose( );
- this._texture = null;
- }
+ // retained for backwards compatibility, should use fgColor
+ set color(fgColor: Color) {
+ if (!sameColor(this._fgColor, fgColor)) {
+ this._fgColor = fgColor;
+ if (this._texture) {
+ this._texture.dispose();
+ this._texture = null;
}
}
+ }
- // retained for backwards compatibility, should use fgColor
- get color( ) : Color {
- return this._fgColor;
- }
+ get fgColor(): Color {
+ return this._fgColor;
+ }
- // retained for backwards compatibility, should use fgColor
- set color( fgColor : Color ) {
- if ( !sameColor( this._fgColor, fgColor ) ) {
- this._fgColor = fgColor;
- if ( this._texture ) {
- this._texture.dispose( );
- this._texture = null;
- }
+ set fgColor(fgColor: Color) {
+ if (!sameColor(this._fgColor, fgColor)) {
+ this._fgColor = fgColor;
+ if (this._texture) {
+ this._texture.dispose();
+ this._texture = null;
}
}
-
- get fgColor( ) : Color {
- return this._fgColor;
- }
+ }
- set fgColor( fgColor : Color ) {
- if ( !sameColor( this._fgColor, fgColor ) ) {
- this._fgColor = fgColor;
- if ( this._texture ) {
- this._texture.dispose( );
- this._texture = null;
- }
- }
- }
-
- get bgColor( ) : Color {
- return this._bgColor;
- }
+ get bgColor(): Color {
+ return this._bgColor;
+ }
- set bgColor( bgColor : Color ) {
- if ( !sameColor( this._bgColor, bgColor ) ) {
- this._bgColor = bgColor;
- }
+ set bgColor(bgColor: Color) {
+ if (!sameColor(this._bgColor, bgColor)) {
+ this._bgColor = bgColor;
}
+ }
- get text( ) : string {
- return this._text;
- }
+ get text(): string {
+ return this._text;
+ }
- set text( text : string ) {
- if ( this._text !== text ) {
- this._text = text;
- if ( this._texture ) {
- this._texture.dispose( );
- this._texture = null;
- }
+ set text(text: string) {
+ if (this._text !== text) {
+ this._text = text;
+ if (this._texture) {
+ this._texture.dispose();
+ this._texture = null;
}
}
+ }
- get texture( ) : TextTexture2D {
- if ( !this._textureFactory ) {
- this._textureFactory = ( this._font ? createTextTextureFactory( this._font ) : null );
- }
- if ( !this._texture ) {
- this._texture = ( this._fgColor && this._text ? this._textureFactory( this._fgColor, this._text ) : null );
- }
- return this._texture;
+ get texture(): TextTexture2D {
+ if (!this._textureFactory) {
+ this._textureFactory = (this._font ? createTextTextureFactory(this._font) : null);
+ }
+ if (!this._texture) {
+ this._texture = (this._fgColor && this._text ? this._textureFactory(this._fgColor, this._text) : null);
}
+ return this._texture;
}
+}
- export function fitToLabel( label : Label ) {
- return function( parentPrefSize : Size ) {
- var texture = label.texture;
- parentPrefSize.w = ( texture ? texture.w : 0 );
- parentPrefSize.h = ( texture ? texture.h : 0 );
- };
- }
+export function fitToLabel(label: Label): LayoutPhase1 {
+ return function (parentPrefSize: Size): void {
+ const texture = label.texture;
+ parentPrefSize.w = (texture ? texture.w : 0);
+ parentPrefSize.h = (texture ? texture.h : 0);
+ };
+}
- export function newLabelPainter( label : Label, xFrac : number, yFrac : number, xAnchor? : number, yAnchor? : number, rotation_CCWRAD? : number ) {
- var textureRenderer = new TextureRenderer( );
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
-
- if ( hasval( label.bgColor ) ) {
- gl.clearColor( label.bgColor.r, label.bgColor.g, label.bgColor.b, label.bgColor.a );
- gl.clear( GL.COLOR_BUFFER_BIT );
- }
-
- var texture = label.texture;
- if ( texture ) {
- textureRenderer.begin( gl, viewport );
- textureRenderer.draw( gl, texture, xFrac, yFrac, { xAnchor: xAnchor, yAnchor: texture.yAnchor( yAnchor ), rotation_CCWRAD: rotation_CCWRAD } );
- textureRenderer.end( gl );
+
+export function newLabelPainter(label: Label, xFrac: number, yFrac: number, xAnchor?: number, yAnchor?: number, rotation_CCWRAD?: number, truncateLabel?: boolean) {
+ const textureRenderer = new TextureRenderer();
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+
+ if (hasval(label.bgColor)) {
+ gl.clearColor(label.bgColor.r, label.bgColor.g, label.bgColor.b, label.bgColor.a);
+ gl.clear(GL.COLOR_BUFFER_BIT);
+ }
+
+ let labelText = label.text;
+ if (truncateLabel) {
+ const xRight = viewport.w - 2 / viewport.w;
+ while (labelText && labelText !== '...') {
+ if (getTextWidth(label.font, labelText) > xRight) {
+ labelText = labelText.substring(0, labelText.length - 4).concat('...');
+ } else {
+ break;
+ }
}
- };
- }
+ }
+
+ label.text = labelText;
+ if (label.texture) {
+ textureRenderer.begin(gl, viewport);
+ textureRenderer.draw(gl, label.texture, xFrac, yFrac, { xAnchor: xAnchor, yAnchor: label.texture.yAnchor(yAnchor), rotation_CCWRAD: rotation_CCWRAD });
+ textureRenderer.end(gl);
+ }
+ };
+}
+
-}
\ No newline at end of file
diff --git a/src/webglimpse/layout/card_layout.ts b/src/webglimpse/layout/card_layout.ts
index a1ed237..5eefe21 100644
--- a/src/webglimpse/layout/card_layout.ts
+++ b/src/webglimpse/layout/card_layout.ts
@@ -27,79 +27,80 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Layout, LayoutEntry } from '../core';
+import { Size, BoundsUnmodifiable } from '../bounds';
+import { hasval } from '../util/util';
- /**
- * A layout similar to overlay_layout except only one child pane is visible at a time.
- * That child pane has its size set to the size of the parent pane. The other children panes
- * are made invisible until they are the active pane.
- *
- * The layoutArg for each child is a boolean, true if it should be the active pane. One is chosen
- * arbitrarily if multiple panes have true layoutArg.
- */
- export function newCardLayout( ): Layout {
+/**
+ * A layout similar to overlay_layout except only one child pane is visible at a time.
+ * That child pane has its size set to the size of the parent pane. The other children panes
+ * are made invisible until they are the active pane.
+ *
+ * The layoutArg for each child is a boolean, true if it should be the active pane. One is chosen
+ * arbitrarily if multiple panes have true layoutArg.
+ */
+export function newCardLayout(): Layout {
- return {
+ return {
- updatePrefSize: function( parentPrefSize : Size, children : LayoutEntry[] ) {
- var activeChild : LayoutEntry;
-
- for ( var c = 0; c < children.length; c++ ) {
- var child = children[ c ];
- var isActive = child.layoutArg;
- if ( isActive ) {
- activeChild = child;
- }
+ updatePrefSize: function (parentPrefSize: Size, children: LayoutEntry[]) {
+ let activeChild: LayoutEntry;
+
+ for (let c = 0; c < children.length; c++) {
+ const child = children[c];
+ const isActive = child.layoutArg;
+ if (isActive) {
+ activeChild = child;
}
+ }
- if ( hasval( activeChild ) ) {
+ if (hasval(activeChild)) {
- var childPrefSize = activeChild.prefSize;
+ const childPrefSize = activeChild.prefSize;
- var childPrefWidth = childPrefSize.w;
- if ( hasval( childPrefWidth ) ) {
- parentPrefSize.w = childPrefWidth;
- }
- else {
- parentPrefSize.w = null;
- }
+ const childPrefWidth = childPrefSize.w;
+ if (hasval(childPrefWidth)) {
+ parentPrefSize.w = childPrefWidth;
+ }
+ else {
+ parentPrefSize.w = null;
+ }
- var childPrefHeight = childPrefSize.h;
- if ( hasval( childPrefHeight ) ) {
- parentPrefSize.h = childPrefHeight;
- }
- else {
- parentPrefSize.h = null;
- }
+ const childPrefHeight = childPrefSize.h;
+ if (hasval(childPrefHeight)) {
+ parentPrefSize.h = childPrefHeight;
}
else {
- parentPrefSize.w = 0;
- parentPrefSize.h = 0;
+ parentPrefSize.h = null;
}
- },
+ }
+ else {
+ parentPrefSize.w = 0;
+ parentPrefSize.h = 0;
+ }
+ },
- updateChildViewports: function( children : LayoutEntry[], parentViewport : BoundsUnmodifiable ) {
- var activeChildIndex : number;
-
- for ( var c = 0; c < children.length; c++ ) {
- var child = children[ c ];
- var isActive = child.layoutArg;
- if ( isActive ) {
- activeChildIndex = c;
- }
+ updateChildViewports: function (children: LayoutEntry[], parentViewport: BoundsUnmodifiable) {
+ let activeChildIndex: number;
+
+ for (let c = 0; c < children.length; c++) {
+ const child = children[c];
+ const isActive = child.layoutArg;
+ if (isActive) {
+ activeChildIndex = c;
}
-
- for ( var c = 0; c < children.length; c++ ) {
- if ( c === activeChildIndex ) {
- children[ c ].viewport.setBounds( parentViewport );
- }
- else {
- children[ c ].viewport.setEdges( 0, 0, 0, 0 );
- }
+ }
+
+ for (let c = 0; c < children.length; c++) {
+ if (c === activeChildIndex) {
+ children[c].viewport.setBounds(parentViewport);
+ }
+ else {
+ children[c].viewport.setEdges(0, 0, 0, 0);
}
}
+ }
- };
- }
-}
\ No newline at end of file
+ };
+}
diff --git a/src/webglimpse/layout/column_layout.ts b/src/webglimpse/layout/column_layout.ts
index 677a70b..0c2416c 100644
--- a/src/webglimpse/layout/column_layout.ts
+++ b/src/webglimpse/layout/column_layout.ts
@@ -27,149 +27,150 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { LayoutEntry, Layout } from '../core';
+import { Size, BoundsUnmodifiable } from '../bounds';
+import { hasval, isNumber } from '../util/util';
- function childWidth( child : LayoutEntry ) : number {
- var usePrefWidth = ( !hasval( child.layoutOptions ) || child.layoutOptions.width === undefined || child.layoutOptions.width === 'pref' );
- return ( usePrefWidth ? child.prefSize.w : child.layoutOptions.width );
- }
+function childWidth(child: LayoutEntry): number {
+ const usePrefWidth = (!hasval(child.layoutOptions) || child.layoutOptions.width === undefined || child.layoutOptions.width === 'pref');
+ return (usePrefWidth ? child.prefSize.w : child.layoutOptions.width);
+}
- export function newColumnLayout( leftToRight : boolean = true ) : Layout {
+export function newColumnLayout(leftToRight: boolean = true): Layout {
- return {
+ return {
- updatePrefSize: function( parentPrefSize : Size, children : LayoutEntry[] ) {
- var childrenToPlace = [ ];
- for ( var c = 0; c < children.length; c++ ) {
- var child = children[ c ];
- if ( isNumber( child.layoutArg ) && !( child.layoutOptions && child.layoutOptions.hide ) ) {
- childrenToPlace.push( child );
- }
+ updatePrefSize: function (parentPrefSize: Size, children: LayoutEntry[]) {
+ const childrenToPlace = [];
+ for (let c = 0; c < children.length; c++) {
+ const child = children[c];
+ if (isNumber(child.layoutArg) && !(child.layoutOptions && child.layoutOptions.hide)) {
+ childrenToPlace.push(child);
}
+ }
- var hMax = 0;
- var wSum = 0;
- for ( var c = 0; c < childrenToPlace.length; c++ ) {
- var child = childrenToPlace[ c ];
-
- var honorChildHeight = !( child.layoutOptions && child.layoutOptions.ignoreHeight );
- if ( honorChildHeight ) {
- var h = child.prefSize.h;
- if ( hasval( hMax ) && hasval( h ) ) {
- hMax = Math.max( hMax, h );
- }
- else {
- hMax = null;
- }
- }
+ let hMax = 0;
+ let wSum = 0;
+ for (let c = 0; c < childrenToPlace.length; c++) {
+ const child = childrenToPlace[c];
- var w = childWidth( child );
- if ( hasval( wSum ) && hasval( w ) ) {
- wSum += w;
- }
- else {
- wSum = null;
- }
- }
- parentPrefSize.w = wSum;
- parentPrefSize.h = hMax;
- },
-
-
- updateChildViewports: function( children : LayoutEntry[], parentViewport : BoundsUnmodifiable ) {
- var childrenToPlace = [ ];
- var childrenToHide = [ ];
- for ( var c = 0; c < children.length; c++ ) {
- var child = children[ c ];
- if ( isNumber( child.layoutArg ) && !( child.layoutOptions && child.layoutOptions.hide ) ) {
- childrenToPlace.push( child );
+ const honorChildHeight = !(child.layoutOptions && child.layoutOptions.ignoreHeight);
+ if (honorChildHeight) {
+ const h = child.prefSize.h;
+ if (hasval(hMax) && hasval(h)) {
+ hMax = Math.max(hMax, h);
}
else {
- childrenToHide.push( child );
+ hMax = null;
}
}
- // Use the original index to make the sort stable
- var indexProp = 'webglimpse_columnLayout_index';
- for ( var c = 0; c < childrenToPlace.length; c++ ) {
- var child = childrenToPlace[ c ];
- child[ indexProp ] = c;
+ const w = childWidth(child);
+ if (hasval(wSum) && hasval(w)) {
+ wSum += w;
}
+ else {
+ wSum = null;
+ }
+ }
+ parentPrefSize.w = wSum;
+ parentPrefSize.h = hMax;
+ },
+
+
+ updateChildViewports: function (children: LayoutEntry[], parentViewport: BoundsUnmodifiable) {
+ const childrenToPlace = [];
+ const childrenToHide = [];
+ for (let c = 0; c < children.length; c++) {
+ const child = children[c];
+ if (isNumber(child.layoutArg) && !(child.layoutOptions && child.layoutOptions.hide)) {
+ childrenToPlace.push(child);
+ }
+ else {
+ childrenToHide.push(child);
+ }
+ }
- childrenToPlace.sort( function( a : LayoutEntry, b : LayoutEntry ) {
- var orderDiff = a.layoutArg - b.layoutArg;
- return ( orderDiff !== 0 ? orderDiff : ( a[ indexProp ] - b[ indexProp ] ) );
- } );
-
- var numFlexible = 0;
- var totalFlexWidth = parentViewport.w;
- for ( var c = 0; c < childrenToPlace.length; c++ ) {
- var w = childWidth( childrenToPlace[ c ] );
- if ( hasval( w ) ) {
- totalFlexWidth -= w;
+ // Use the original index to make the sort stable
+ const indexProp = 'webglimpse_columnLayout_index';
+ for (let c = 0; c < childrenToPlace.length; c++) {
+ const child = childrenToPlace[c];
+ child[indexProp] = c;
+ }
+
+ childrenToPlace.sort(function (a: LayoutEntry, b: LayoutEntry) {
+ const orderDiff = a.layoutArg - b.layoutArg;
+ return (orderDiff !== 0 ? orderDiff : (a[indexProp] - b[indexProp]));
+ });
+
+ let numFlexible = 0;
+ let totalFlexWidth = parentViewport.w;
+ for (let c = 0; c < childrenToPlace.length; c++) {
+ const w = childWidth(childrenToPlace[c]);
+ if (hasval(w)) {
+ totalFlexWidth -= w;
+ }
+ else {
+ numFlexible++;
+ }
+ }
+ const flexWidth = totalFlexWidth / numFlexible;
+
+ if (leftToRight) {
+ const jStart = parentViewport.jStart;
+ const jEnd = parentViewport.jEnd;
+ let iStart = parentViewport.iStart;
+ let iRemainder = 0;
+ for (let c = 0; c < childrenToPlace.length; c++) {
+ const child = childrenToPlace[c];
+
+ let iEnd: number;
+ const w = childWidth(child);
+ if (hasval(w)) {
+ iEnd = iStart + w;
}
else {
- numFlexible++;
+ const iEnd0 = iStart + flexWidth + iRemainder;
+ iEnd = Math.round(iEnd0);
+ iRemainder = iEnd0 - iEnd;
}
+
+ child.viewport.setEdges(iStart, iEnd, jStart, jEnd);
+ iStart = iEnd;
}
- var flexWidth = totalFlexWidth / numFlexible;
-
- if ( leftToRight ) {
- var jStart = parentViewport.jStart;
- var jEnd = parentViewport.jEnd;
- var iStart = parentViewport.iStart;
- var iRemainder = 0;
- for ( var c = 0; c < childrenToPlace.length; c++ ) {
- var child = childrenToPlace[ c ];
-
- var iEnd : number;
- var w = childWidth( child );
- if ( hasval( w ) ) {
- iEnd = iStart + w;
- }
- else {
- var iEnd0 = iStart + flexWidth + iRemainder;
- iEnd = Math.round( iEnd0 );
- iRemainder = iEnd0 - iEnd;
- }
-
- child.viewport.setEdges( iStart, iEnd, jStart, jEnd );
- iStart = iEnd;
+ }
+ else {
+ const jStart = parentViewport.jStart;
+ const jEnd = parentViewport.jEnd;
+ let iEnd = parentViewport.iEnd;
+ let iRemainder = 0;
+ for (let c = 0; c < childrenToPlace.length; c++) {
+ const child = childrenToPlace[c];
+
+ let iStart: number;
+ const w = childWidth(child);
+ if (hasval(w)) {
+ iStart = iEnd - w;
}
- }
- else {
- var jStart = parentViewport.jStart;
- var jEnd = parentViewport.jEnd;
- var iEnd = parentViewport.iEnd;
- var iRemainder = 0;
- for ( var c = 0; c < childrenToPlace.length; c++ ) {
- var child = childrenToPlace[ c ];
-
- var iStart : number;
- var w = childWidth( child );
- if ( hasval( w ) ) {
- iStart = iEnd - w;
- }
- else {
- var iStart0 = iEnd - flexWidth - iRemainder;
- iStart = Math.round( iStart0 );
- iRemainder = iStart - iStart0;
- }
-
- child.viewport.setEdges( iStart, iEnd, jStart, jEnd );
- iEnd = iStart;
+ else {
+ const iStart0 = iEnd - flexWidth - iRemainder;
+ iStart = Math.round(iStart0);
+ iRemainder = iStart - iStart0;
}
- }
- for ( var c = 0; c < childrenToHide.length; c++ ) {
- childrenToHide[ c ].viewport.setEdges( 0, 0, 0, 0 );
+ child.viewport.setEdges(iStart, iEnd, jStart, jEnd);
+ iEnd = iStart;
}
}
+ for (let c = 0; c < childrenToHide.length; c++) {
+ childrenToHide[c].viewport.setEdges(0, 0, 0, 0);
+ }
+ }
+
- };
- }
-}
\ No newline at end of file
+ };
+}
diff --git a/src/webglimpse/layout/corner_layout.ts b/src/webglimpse/layout/corner_layout.ts
index 9fe4205..eb743d4 100644
--- a/src/webglimpse/layout/corner_layout.ts
+++ b/src/webglimpse/layout/corner_layout.ts
@@ -27,57 +27,59 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Side } from '../misc';
+import { Layout, LayoutEntry } from '../core';
+import { Size, BoundsUnmodifiable } from '../bounds';
+import { hasval } from '../util/util';
- export function newCornerLayout( hSide : Side, vSide : Side ) : Layout {
- return {
- updatePrefSize: function( parentPrefSize : Size, children : LayoutEntry[] ) {
- if ( children.length === 1 ) {
- var childPrefSize = children[ 0 ].prefSize;
- parentPrefSize.w = childPrefSize.w;
- parentPrefSize.h = childPrefSize.h;
+export function newCornerLayout(hSide: Side, vSide: Side): Layout {
+ return {
+ updatePrefSize: function (parentPrefSize: Size, children: LayoutEntry[]) {
+ if (children.length === 1) {
+ const childPrefSize = children[0].prefSize;
+ parentPrefSize.w = childPrefSize.w;
+ parentPrefSize.h = childPrefSize.h;
+ }
+ else if (children.length > 1) {
+ throw new Error('Corner layout only works with 1 child, but pane has ' + this.children.length + ' children');
+ }
+ },
+ updateChildViewports: function (children: LayoutEntry[], parentViewport: BoundsUnmodifiable) {
+ if (children.length === 1) {
+ const child = children[0];
+
+ let iStart;
+ let iEnd;
+ const w = child.prefSize.w;
+ if (hSide === Side.RIGHT) {
+ iEnd = parentViewport.iEnd;
+ iStart = (hasval(w) ? Math.max(iEnd - w, parentViewport.iStart) : parentViewport.iStart);
}
- else if ( children.length > 1 ) {
- throw new Error( 'Corner layout only works with 1 child, but pane has ' + this.children.length + ' children' );
+ else {
+ iStart = parentViewport.iStart;
+ iEnd = (hasval(w) ? Math.min(iStart + w, parentViewport.iEnd) : parentViewport.iEnd);
}
- },
- updateChildViewports: function( children : LayoutEntry[], parentViewport : BoundsUnmodifiable ) {
- if ( children.length === 1 ) {
- var child = children[ 0 ];
-
- var iStart;
- var iEnd;
- var w = child.prefSize.w;
- if ( hSide === Side.RIGHT ) {
- iEnd = parentViewport.iEnd;
- iStart = ( hasval( w ) ? Math.max( iEnd-w, parentViewport.iStart ) : parentViewport.iStart );
- }
- else {
- iStart = parentViewport.iStart;
- iEnd = ( hasval( w ) ? Math.min( iStart+w, parentViewport.iEnd ) : parentViewport.iEnd );
- }
- var jStart;
- var jEnd;
- var h = child.prefSize.h;
- if ( vSide === Side.BOTTOM ) {
- jStart = parentViewport.jStart;
- jEnd = ( hasval( h ) ? Math.min( jStart+h, parentViewport.jEnd ) : parentViewport.jEnd );
- }
- else {
- jEnd = parentViewport.jEnd;
- jStart = ( hasval( h ) ? Math.max( jEnd-h, parentViewport.jStart ) : parentViewport.jStart );
- }
-
- child.viewport.setEdges( iStart, iEnd, jStart, jEnd );
+ let jStart;
+ let jEnd;
+ const h = child.prefSize.h;
+ if (vSide === Side.BOTTOM) {
+ jStart = parentViewport.jStart;
+ jEnd = (hasval(h) ? Math.min(jStart + h, parentViewport.jEnd) : parentViewport.jEnd);
}
- else if ( children.length > 1 ) {
- throw new Error( 'Corner layout only works with 1 child, but pane has ' + this.children.length + ' children' );
+ else {
+ jEnd = parentViewport.jEnd;
+ jStart = (hasval(h) ? Math.max(jEnd - h, parentViewport.jStart) : parentViewport.jStart);
}
+
+ child.viewport.setEdges(iStart, iEnd, jStart, jEnd);
+ }
+ else if (children.length > 1) {
+ throw new Error('Corner layout only works with 1 child, but pane has ' + this.children.length + ' children');
}
- };
- }
+ }
+ };
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/layout/inset_layout.ts b/src/webglimpse/layout/inset_layout.ts
index cfe90db..50a29db 100644
--- a/src/webglimpse/layout/inset_layout.ts
+++ b/src/webglimpse/layout/inset_layout.ts
@@ -27,98 +27,101 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Layout, LayoutEntry, Pane } from '../core';
+import { Size, BoundsUnmodifiable } from '../bounds';
+import { Color } from '../color';
+import { newBackgroundPainter } from '../misc';
+import { hasval } from '../util/util';
- export interface Insets {
- top : number;
- right : number;
- bottom : number;
- left : number;
- }
+export interface Insets {
+ top: number;
+ right: number;
+ bottom: number;
+ left: number;
+}
- export function newInsets( ...insets : number[] ) : Insets {
- switch ( insets.length ) {
+export function newInsets(...insets: number[]): Insets {
+ switch (insets.length) {
- case 1:
- return {
- top: insets[ 0 ],
- right: insets[ 0 ],
- bottom: insets[ 0 ],
- left: insets[ 0 ]
- };
+ case 1:
+ return {
+ top: insets[0],
+ right: insets[0],
+ bottom: insets[0],
+ left: insets[0]
+ };
- case 2:
- return {
- top: insets[ 0 ],
- right: insets[ 1 ],
- bottom: insets[ 0 ],
- left: insets[ 1 ]
- };
+ case 2:
+ return {
+ top: insets[0],
+ right: insets[1],
+ bottom: insets[0],
+ left: insets[1]
+ };
- case 3:
- return {
- top: insets[ 0 ],
- right: insets[ 1 ],
- bottom: insets[ 2 ],
- left: insets[ 1 ]
- };
+ case 3:
+ return {
+ top: insets[0],
+ right: insets[1],
+ bottom: insets[2],
+ left: insets[1]
+ };
- case 4:
- return {
- top: insets[ 0 ],
- right: insets[ 1 ],
- bottom: insets[ 2 ],
- left: insets[ 3 ]
- };
+ case 4:
+ return {
+ top: insets[0],
+ right: insets[1],
+ bottom: insets[2],
+ left: insets[3]
+ };
- default:
- throw new Error( 'Expected 1, 2, 3, or 4 args, but found ' + insets.length );
- }
+ default:
+ throw new Error('Expected 1, 2, 3, or 4 args, but found ' + insets.length);
}
+}
- export function newInsetLayout( insets : Insets ) : Layout {
- return {
- updatePrefSize: function( parentPrefSize : Size, children : LayoutEntry[] ) {
- if ( children.length === 0 ) {
- parentPrefSize.w = insets.left + insets.right;
- parentPrefSize.h = insets.top + insets.bottom;
- }
- else if ( children.length === 1 ) {
- var childPrefSize = children[ 0 ].prefSize;
- parentPrefSize.w = ( hasval( childPrefSize.w ) ? childPrefSize.w + insets.left + insets.right : null );
- parentPrefSize.h = ( hasval( childPrefSize.h ) ? childPrefSize.h + insets.top + insets.bottom : null );
- }
- else if ( children.length > 1 ) {
- throw new Error( 'Inset layout works with at most 1 child, but pane has ' + this.children.length + ' children' );
- }
- },
- updateChildViewports: function( children : LayoutEntry[], parentViewport : BoundsUnmodifiable ) {
- if ( children.length === 1 ) {
- var childViewport = children[ 0 ].viewport;
- childViewport.setEdges( parentViewport.iStart + insets.left,
- parentViewport.iEnd - insets.right,
- parentViewport.jStart + insets.bottom,
- parentViewport.jEnd - insets.top );
- }
- else if ( children.length > 1 ) {
- throw new Error( 'Inset layout works with at most 1 child, but pane has ' + this.children.length + ' children' );
- }
+export function newInsetLayout(insets: Insets): Layout {
+ return {
+ updatePrefSize: function (parentPrefSize: Size, children: LayoutEntry[]) {
+ if (children.length === 0) {
+ parentPrefSize.w = insets.left + insets.right;
+ parentPrefSize.h = insets.top + insets.bottom;
}
- };
- }
+ else if (children.length === 1) {
+ const childPrefSize = children[0].prefSize;
+ parentPrefSize.w = (hasval(childPrefSize.w) ? childPrefSize.w + insets.left + insets.right : null);
+ parentPrefSize.h = (hasval(childPrefSize.h) ? childPrefSize.h + insets.top + insets.bottom : null);
+ }
+ else if (children.length > 1) {
+ throw new Error('Inset layout works with at most 1 child, but pane has ' + this.children.length + ' children');
+ }
+ },
+ updateChildViewports: function (children: LayoutEntry[], parentViewport: BoundsUnmodifiable) {
+ if (children.length === 1) {
+ const childViewport = children[0].viewport;
+ childViewport.setEdges(parentViewport.iStart + insets.left,
+ parentViewport.iEnd - insets.right,
+ parentViewport.jStart + insets.bottom,
+ parentViewport.jEnd - insets.top);
+ }
+ else if (children.length > 1) {
+ throw new Error('Inset layout works with at most 1 child, but pane has ' + this.children.length + ' children');
+ }
+ }
+ };
+}
- export function newInsetPane( pane : Pane, insets : Insets, bgColor : Color = null, consumeInputEvents : boolean = true ) : Pane {
- var insetPane = new Pane( newInsetLayout( insets ), consumeInputEvents );
- if ( hasval( bgColor ) ) {
- insetPane.addPainter( newBackgroundPainter( bgColor ) );
- }
- insetPane.addPane( pane );
- return insetPane;
+export function newInsetPane(pane: Pane, insets: Insets, bgColor: Color = null, consumeInputEvents: boolean = true): Pane {
+ const insetPane = new Pane(newInsetLayout(insets), consumeInputEvents);
+ if (hasval(bgColor)) {
+ insetPane.addPainter(newBackgroundPainter(bgColor));
}
+ insetPane.addPane(pane);
+ return insetPane;
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/layout/overlay_layout.ts b/src/webglimpse/layout/overlay_layout.ts
index 8784aa5..41ba711 100644
--- a/src/webglimpse/layout/overlay_layout.ts
+++ b/src/webglimpse/layout/overlay_layout.ts
@@ -27,64 +27,65 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Layout, LayoutEntry } from '../core';
+import { Size, BoundsUnmodifiable } from '../bounds';
+import { isEmpty, hasval } from '../util/util';
- /**
- * Simple layout which sets the sizes of all child panes to the size of the parent pane
- * (causing all the children to 'overlay' each other and the parent).
- */
- export function newOverlayLayout( ): Layout {
+/**
+ * Simple layout which sets the sizes of all child panes to the size of the parent pane
+ * (causing all the children to 'overlay' each other and the parent).
+ */
+export function newOverlayLayout(): Layout {
- return {
+ return {
- updatePrefSize: function( parentPrefSize : Size, children : LayoutEntry[] ) {
- var underlays : LayoutEntry[] = [];
- for ( var c = 0; c < children.length; c++ ) {
- var child = children[ c ];
- var isUnderlay = child.layoutArg;
- if ( isUnderlay ) {
- underlays.push( child );
- }
+ updatePrefSize: function (parentPrefSize: Size, children: LayoutEntry[]) {
+ const underlays: LayoutEntry[] = [];
+ for (let c = 0; c < children.length; c++) {
+ const child = children[c];
+ const isUnderlay = child.layoutArg;
+ if (isUnderlay) {
+ underlays.push(child);
}
+ }
- if ( !isEmpty( underlays ) ) {
- var maxChildPrefWidth = 0;
- var maxChildPrefHeight = 0;
- for ( var c = 0; c < underlays.length; c++ ) {
- var childPrefSize = underlays[ c ].prefSize;
+ if (!isEmpty(underlays)) {
+ let maxChildPrefWidth = 0;
+ let maxChildPrefHeight = 0;
+ for (let c = 0; c < underlays.length; c++) {
+ const childPrefSize = underlays[c].prefSize;
- var childPrefWidth = childPrefSize.w;
- if ( hasval( maxChildPrefWidth ) && hasval( childPrefWidth ) ) {
- maxChildPrefWidth = Math.max( maxChildPrefWidth, childPrefWidth );
- }
- else {
- maxChildPrefWidth = null;
- }
+ const childPrefWidth = childPrefSize.w;
+ if (hasval(maxChildPrefWidth) && hasval(childPrefWidth)) {
+ maxChildPrefWidth = Math.max(maxChildPrefWidth, childPrefWidth);
+ }
+ else {
+ maxChildPrefWidth = null;
+ }
- var childPrefHeight = childPrefSize.h;
- if ( hasval( maxChildPrefHeight ) && hasval( childPrefHeight ) ) {
- maxChildPrefHeight = Math.max( maxChildPrefHeight, childPrefHeight );
- }
- else {
- maxChildPrefHeight = null;
- }
+ const childPrefHeight = childPrefSize.h;
+ if (hasval(maxChildPrefHeight) && hasval(childPrefHeight)) {
+ maxChildPrefHeight = Math.max(maxChildPrefHeight, childPrefHeight);
+ }
+ else {
+ maxChildPrefHeight = null;
}
- parentPrefSize.w = maxChildPrefWidth;
- parentPrefSize.h = maxChildPrefHeight;
- }
- else {
- parentPrefSize.w = 0;
- parentPrefSize.h = 0;
}
- },
+ parentPrefSize.w = maxChildPrefWidth;
+ parentPrefSize.h = maxChildPrefHeight;
+ }
+ else {
+ parentPrefSize.w = 0;
+ parentPrefSize.h = 0;
+ }
+ },
- updateChildViewports: function( children : LayoutEntry[], parentViewport : BoundsUnmodifiable ) {
- for ( var c = 0; c < children.length; c++ ) {
- children[ c ].viewport.setBounds( parentViewport );
- }
+ updateChildViewports: function (children: LayoutEntry[], parentViewport: BoundsUnmodifiable) {
+ for (let c = 0; c < children.length; c++) {
+ children[c].viewport.setBounds(parentViewport);
}
+ }
- };
- }
-}
\ No newline at end of file
+ };
+}
diff --git a/src/webglimpse/layout/row_layout.ts b/src/webglimpse/layout/row_layout.ts
index ebad070..03508be 100644
--- a/src/webglimpse/layout/row_layout.ts
+++ b/src/webglimpse/layout/row_layout.ts
@@ -27,188 +27,189 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
-
- // 'pref' indicates the child's height should be its preferred height
- // 'pref-max' indicates the child's height should be at most its preferred height, but should be made smaller
- // (treated as a flex with child.layoutOptions.height === null) if their is insufficient room in the
- // parentViewport to accommodate all children at their set size
- //
- // This layout is essentially a generalization of timeline_layout.ts. If you use a row_layout with the center element set to pref-max
- // you get the same effect as timeline_layout).
- function childHeight( child : LayoutEntry ) : number {
- var usePrefHeight = ( !hasval( child.layoutOptions ) || child.layoutOptions.height === undefined || child.layoutOptions.height === 'pref' || child.layoutOptions.height === 'pref-max' );
- return ( usePrefHeight ? child.prefSize.h : child.layoutOptions.height );
+import { LayoutEntry, Layout } from '../core';
+import { BoundsUnmodifiable, Size } from '../bounds';
+import { hasval, isNumber } from '../util/util';
+
+
+// 'pref' indicates the child's height should be its preferred height
+// 'pref-max' indicates the child's height should be at most its preferred height, but should be made smaller
+// (treated as a flex with child.layoutOptions.height === null) if their is insufficient room in the
+// parentViewport to accommodate all children at their set size
+//
+// This layout is essentially a generalization of timeline_layout.ts. If you use a row_layout with the center element set to pref-max
+// you get the same effect as timeline_layout).
+function childHeight(child: LayoutEntry): number {
+ const usePrefHeight = (!hasval(child.layoutOptions) || child.layoutOptions.height === undefined || child.layoutOptions.height === 'pref' || child.layoutOptions.height === 'pref-max');
+ return (usePrefHeight ? child.prefSize.h : child.layoutOptions.height);
+}
+
+// see above, like childHeight( ) but don't count 'pref-max'
+function childHeightOverfull(child: LayoutEntry): number {
+ const usePrefHeight = (!hasval(child.layoutOptions) || child.layoutOptions.height === undefined || child.layoutOptions.height === 'pref');
+
+ if (usePrefHeight) {
+ return child.prefSize.h;
}
-
- // see above, like childHeight( ) but don't count 'pref-max'
- function childHeightOverfull( child : LayoutEntry ) : number {
- var usePrefHeight = ( !hasval( child.layoutOptions ) || child.layoutOptions.height === undefined || child.layoutOptions.height === 'pref' );
-
- if ( usePrefHeight ) {
- return child.prefSize.h;
- }
- else if ( child.layoutOptions.height == 'pref-max' ) {
- return null;
+ else if (child.layoutOptions.height === 'pref-max') {
+ return null;
+ }
+ else {
+ return child.layoutOptions.height;
+ }
+}
+
+interface FlexData {
+ numFlexible: number;
+ totalHeight: number;
+ totalFlexHeight: number;
+ flexHeight: number;
+ childHeight: (child: LayoutEntry) => number;
+}
+
+function calculateFlexData(childrenToPlace: LayoutEntry[], parentViewport: BoundsUnmodifiable, getChildHeight: (child: LayoutEntry) => number): FlexData {
+ let numFlexible = 0;
+ let totalHeight = 0;
+ for (let c = 0; c < childrenToPlace.length; c++) {
+ const h = getChildHeight(childrenToPlace[c]);
+ if (hasval(h)) {
+ totalHeight += h;
}
else {
- return child.layoutOptions.height;
+ numFlexible++;
}
}
+ const totalFlexHeight = parentViewport.h - totalHeight;
+ const flexHeight = totalFlexHeight / numFlexible;
+ return { numFlexible: numFlexible, totalHeight: totalHeight, flexHeight: flexHeight, totalFlexHeight: totalFlexHeight, childHeight: getChildHeight };
+}
- interface FlexData {
- numFlexible : number;
- totalHeight : number;
- totalFlexHeight : number;
- flexHeight : number;
- childHeight : ( child : LayoutEntry ) => number;
- }
-
- function calculateFlexData( childrenToPlace : LayoutEntry[], parentViewport : BoundsUnmodifiable, childHeight : ( child : LayoutEntry ) => number ) : FlexData {
- var numFlexible = 0;
- var totalHeight = 0;
- for ( var c = 0; c < childrenToPlace.length; c++ ) {
- var h = childHeight( childrenToPlace[ c ] );
- if ( hasval( h ) ) {
- totalHeight += h;
- }
- else {
- numFlexible++;
- }
- }
- var totalFlexHeight = parentViewport.h - totalHeight;
- var flexHeight = totalFlexHeight / numFlexible;
- return { numFlexible : numFlexible, totalHeight : totalHeight, flexHeight : flexHeight, totalFlexHeight : totalFlexHeight, childHeight : childHeight };
- }
-
- export function newRowLayout( topToBottom : boolean = true ) : Layout {
+export function newRowLayout(topToBottom: boolean = true): Layout {
- return {
+ return {
- updatePrefSize: function( parentPrefSize : Size, children : LayoutEntry[] ) {
- var childrenToPlace = [ ];
- for ( var c = 0; c < children.length; c++ ) {
- var child = children[ c ];
- if ( isNumber( child.layoutArg ) && !( child.layoutOptions && child.layoutOptions.hide ) ) {
- childrenToPlace.push( child );
- }
+ updatePrefSize: function (parentPrefSize: Size, children: LayoutEntry[]) {
+ const childrenToPlace = [];
+ for (let c = 0; c < children.length; c++) {
+ const child = children[c];
+ if (isNumber(child.layoutArg) && !(child.layoutOptions && child.layoutOptions.hide)) {
+ childrenToPlace.push(child);
}
+ }
- var wMax = 0;
- var hSum = 0;
- for ( var c = 0; c < childrenToPlace.length; c++ ) {
- var child = childrenToPlace[ c ];
-
- var honorChildWidth = !( child.layoutOptions && child.layoutOptions.ignoreWidth );
- if ( honorChildWidth ) {
- var w = child.prefSize.w;
- if ( hasval( wMax ) && hasval( w ) ) {
- wMax = Math.max( wMax, w );
- }
- else {
- wMax = null;
- }
- }
+ let wMax = 0;
+ let hSum = 0;
+ for (let c = 0; c < childrenToPlace.length; c++) {
+ const child = childrenToPlace[c];
- var h = childHeight( child );
- if ( hasval( hSum ) && hasval( h ) ) {
- hSum += h;
- }
- else {
- hSum = null;
- }
- }
- parentPrefSize.w = wMax;
- parentPrefSize.h = hSum;
- },
-
-
- updateChildViewports: function( children : LayoutEntry[], parentViewport : BoundsUnmodifiable ) {
- var childrenToPlace = [ ];
- var childrenToHide = [ ];
- for ( var c = 0; c < children.length; c++ ) {
- var child = children[ c ];
- if ( isNumber( child.layoutArg ) && !( child.layoutOptions && child.layoutOptions.hide ) ) {
- childrenToPlace.push( child );
+ const honorChildWidth = !(child.layoutOptions && child.layoutOptions.ignoreWidth);
+ if (honorChildWidth) {
+ const w = child.prefSize.w;
+ if (hasval(wMax) && hasval(w)) {
+ wMax = Math.max(wMax, w);
}
else {
- childrenToHide.push( child );
+ wMax = null;
}
}
- // Use the original index to make the sort stable
- var indexProp = 'webglimpse_rowLayout_index';
- for ( var c = 0; c < childrenToPlace.length; c++ ) {
- var child = childrenToPlace[ c ];
- child[ indexProp ] = c;
+ const h = childHeight(child);
+ if (hasval(hSum) && hasval(h)) {
+ hSum += h;
}
-
- childrenToPlace.sort( function( a : LayoutEntry, b : LayoutEntry ) {
- var orderDiff = a.layoutArg - b.layoutArg;
- return ( orderDiff !== 0 ? orderDiff : ( a[ indexProp ] - b[ indexProp ] ) );
- } );
-
- // calculate assuming sufficient space
- var flexData = calculateFlexData( children, parentViewport, childHeight );
-
- // recalculate allowing 'pref-max' children to shrink if insufficient space
- if ( flexData.totalHeight > parentViewport.h ) {
- flexData = calculateFlexData( children, parentViewport, childHeightOverfull );
+ else {
+ hSum = null;
}
-
- if ( topToBottom ) {
- var iStart = parentViewport.iStart;
- var iEnd = parentViewport.iEnd;
- var jEnd = parentViewport.jEnd;
- var jRemainder = 0;
- for ( var c = 0; c < childrenToPlace.length; c++ ) {
- var child = childrenToPlace[ c ];
-
- var jStart : number;
- var h = flexData.childHeight( child );
- if ( hasval( h ) ) {
- jStart = jEnd - h;
- }
- else {
- var jStart0 = jEnd - flexData.flexHeight - jRemainder;
- jStart = Math.round( jStart0 );
- jRemainder = jStart - jStart0;
- }
-
- child.viewport.setEdges( iStart, iEnd, jStart, jEnd );
- jEnd = jStart;
- }
+ }
+ parentPrefSize.w = wMax;
+ parentPrefSize.h = hSum;
+ },
+
+
+ updateChildViewports: function (children: LayoutEntry[], parentViewport: BoundsUnmodifiable) {
+ const childrenToPlace = [];
+ const childrenToHide = [];
+ for (let c = 0; c < children.length; c++) {
+ const child = children[c];
+ if (isNumber(child.layoutArg) && !(child.layoutOptions && child.layoutOptions.hide)) {
+ childrenToPlace.push(child);
}
else {
- var iStart = parentViewport.iStart;
- var iEnd = parentViewport.iEnd;
- var jStart = parentViewport.jStart;
- var jRemainder = 0;
- for ( var c = 0; c < childrenToPlace.length; c++ ) {
- var child = childrenToPlace[ c ];
-
- var jEnd : number;
- var h = flexData.childHeight( child );
- if ( hasval( h ) ) {
- jEnd = jStart + h;
- }
- else {
- var jEnd0 = jStart + flexData.flexHeight + jRemainder;
- jEnd = Math.round( jEnd0 );
- jRemainder = jEnd0 - jEnd;
- }
-
- child.viewport.setEdges( iStart, iEnd, jStart, jEnd );
- jStart = jEnd;
+ childrenToHide.push(child);
+ }
+ }
+
+ // Use the original index to make the sort stable
+ const indexProp = 'webglimpse_rowLayout_index';
+ for (let c = 0; c < childrenToPlace.length; c++) {
+ const child = childrenToPlace[c];
+ child[indexProp] = c;
+ }
+
+ childrenToPlace.sort(function (a: LayoutEntry, b: LayoutEntry) {
+ const orderDiff = a.layoutArg - b.layoutArg;
+ return (orderDiff !== 0 ? orderDiff : (a[indexProp] - b[indexProp]));
+ });
+
+ // calculate assuming sufficient space
+ let flexData = calculateFlexData(children, parentViewport, childHeight);
+
+ // recalculate allowing 'pref-max' children to shrink if insufficient space
+ if (flexData.totalHeight > parentViewport.h) {
+ flexData = calculateFlexData(children, parentViewport, childHeightOverfull);
+ }
+
+ if (topToBottom) {
+ const iStart = parentViewport.iStart;
+ const iEnd = parentViewport.iEnd;
+ let jEnd = parentViewport.jEnd;
+ let jRemainder = 0;
+ for (let c = 0; c < childrenToPlace.length; c++) {
+ const child = childrenToPlace[c];
+
+ let jStart: number;
+ const h = flexData.childHeight(child);
+ if (hasval(h)) {
+ jStart = jEnd - h;
+ }
+ else {
+ const jStart0 = jEnd - flexData.flexHeight - jRemainder;
+ jStart = Math.round(jStart0);
+ jRemainder = jStart - jStart0;
}
+
+ child.viewport.setEdges(iStart, iEnd, jStart, jEnd);
+ jEnd = jStart;
}
+ }
+ else {
+ const iStart = parentViewport.iStart;
+ const iEnd = parentViewport.iEnd;
+ let jStart = parentViewport.jStart;
+ let jRemainder = 0;
+ for (let c = 0; c < childrenToPlace.length; c++) {
+ const child = childrenToPlace[c];
+
+ let jEnd: number;
+ const h = flexData.childHeight(child);
+ if (hasval(h)) {
+ jEnd = jStart + h;
+ }
+ else {
+ const jEnd0 = jStart + flexData.flexHeight + jRemainder;
+ jEnd = Math.round(jEnd0);
+ jRemainder = jEnd0 - jEnd;
+ }
- for ( var c = 0; c < childrenToHide.length; c++ ) {
- childrenToHide[ c ].viewport.setEdges( 0, 0, 0, 0 );
+ child.viewport.setEdges(iStart, iEnd, jStart, jEnd);
+ jStart = jEnd;
}
}
- };
- }
-}
\ No newline at end of file
+
+ for (let c = 0; c < childrenToHide.length; c++) {
+ childrenToHide[c].viewport.setEdges(0, 0, 0, 0);
+ }
+ }
+ };
+}
diff --git a/src/webglimpse/matrix.ts b/src/webglimpse/matrix.ts
index 8de7db1..29818c2 100644
--- a/src/webglimpse/matrix.ts
+++ b/src/webglimpse/matrix.ts
@@ -27,31 +27,30 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { BoundsUnmodifiable } from './bounds';
+import { Axis2D } from './plot/axis';
+// see: http://www.cs.rit.edu/usr/local/pub/wrc/graphics/doc/opengl/books/blue/glOrtho.html
+// see: http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho
+export function glOrtho(left: number, right: number, bottom: number, top: number, near: number, far: number): Float32Array {
- // see: http://www.cs.rit.edu/usr/local/pub/wrc/graphics/doc/opengl/books/blue/glOrtho.html
- // see: http://www.songho.ca/opengl/gl_projectionmatrix.html#ortho
- export function glOrtho( left : number, right : number, bottom : number, top : number, near : number, far : number ) : Float32Array {
+ const tx = (right + left) / (right - left);
+ const ty = (top + bottom) / (top - bottom);
+ const tz = (far + near) / (far - near);
- var tx = ( right + left ) / ( right - left );
- var ty = ( top + bottom ) / ( top - bottom );
- var tz = ( far + near ) / ( far - near );
+ // GL ES (and therefore WebGL) requires matrices to be column-major
+ return new Float32Array([
+ 2 / (right - left), 0, 0, 0,
+ 0, 2 / (top - bottom), 0, 0,
+ 0, 0, -2 / (far - near), 0,
+ -tx, -ty, -tz, 1
+ ]);
+}
- // GL ES (and therefore WebGL) requires matrices to be column-major
- return new Float32Array( [
- 2 / ( right - left ), 0, 0, 0,
- 0, 2 / ( top - bottom ), 0, 0,
- 0, 0, -2 / ( far - near ), 0,
- -tx, -ty, -tz, 1
- ] );
- }
-
- export function glOrthoViewport( viewport : BoundsUnmodifiable ) : Float32Array {
- return glOrtho( -0.5, viewport.w-0.5, -0.5, viewport.h-0.5, -1, 1 );
- }
-
- export function glOrthoAxis( axis : Axis2D ) : Float32Array {
- return glOrtho( axis.xAxis.vMin, axis.xAxis.vMax, axis.yAxis.vMin, axis.yAxis.vMax, -1, 1 );
- }
-}
\ No newline at end of file
+export function glOrthoViewport(viewport: BoundsUnmodifiable): Float32Array {
+ return glOrtho(-0.5, viewport.w - 0.5, -0.5, viewport.h - 0.5, -1, 1);
+}
+
+export function glOrthoAxis(axis: Axis2D): Float32Array {
+ return glOrtho(axis.xAxis.vMin, axis.xAxis.vMax, axis.yAxis.vMin, axis.yAxis.vMax, -1, 1);
+}
diff --git a/src/webglimpse/misc.ts b/src/webglimpse/misc.ts
index 77095a7..b76cda2 100644
--- a/src/webglimpse/misc.ts
+++ b/src/webglimpse/misc.ts
@@ -27,350 +27,474 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Painter, Pane } from './core';
+import { BoundsUnmodifiable, Size } from './bounds';
+import { Color, sameColor } from './color';
+import { Program, UniformColor, Attribute, Uniform1f } from './shader';
+import { newDynamicBuffer, DynamicBuffer } from './buffer';
+import { GL, concatLines, hasval } from './util/util';
+import { Texture2D, TextureDrawOptions, TextureRenderer } from './texture';
+import { Notification } from './util/notification';
+
+export function newGroupPainter(...painters: Painter[]): Painter {
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable): void {
+ for (let n = 0; n < painters.length; n++) {
+ painters[n](gl, viewport);
+ }
+ };
+}
- export function newGroupPainter( ...painters : Painter[] ) : Painter {
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- for ( var n = 0; n < painters.length; n++ ) {
- painters[ n ]( gl, viewport );
- }
- };
+export function newBlendingBackgroundPainter(color: Color): Painter {
+
+ const program = new Program(xyNdc_VERTSHADER, solid_FRAGSHADER);
+ const u_Color = new UniformColor(program, 'u_Color');
+ const a_XyNdc = new Attribute(program, 'a_XyNdc');
+
+ const numVertices = 4;
+ const xy_NDC = new Float32Array(2 * numVertices);
+ const xyBuffer_NDC = newDynamicBuffer();
+
+ return function (gl: WebGLRenderingContext): void {
+ if (color.a >= 1) {
+ gl.disable(GL.BLEND);
+ }
+ else {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+ }
+
+ program.use(gl);
+ u_Color.setData(gl, color);
+
+ xy_NDC[0] = -1;
+ xy_NDC[1] = 1;
+ xy_NDC[2] = -1;
+ xy_NDC[3] = -1;
+ xy_NDC[4] = 1;
+ xy_NDC[5] = 1;
+ xy_NDC[6] = 1;
+ xy_NDC[7] = -1;
+
+ xyBuffer_NDC.setData(xy_NDC);
+ a_XyNdc.setDataAndEnable(gl, xyBuffer_NDC, 2, GL.FLOAT);
+
+ gl.drawArrays(GL.TRIANGLE_STRIP, 0, numVertices);
+
+ a_XyNdc.disable(gl);
+ program.endUse(gl);
+ };
+}
+
+export class Highlight {
+
+ private _color: Color;
+ private _dashPattern = 0xFFFF;
+ private _dashLength = 16;
+
+ program = new Program(xyNdc_VERTSHADER, dash2_FRAGSHADER);
+ u_Color = new UniformColor(this.program, 'u_Color');
+ a_XyNdc = new Attribute(this.program, 'a_XyNdc');
+ u_yOffset = new Uniform1f(this.program, 'u_yOffset');
+ u_DashPattern = new Uniform1f(this.program, 'u_DashPattern');
+ u_DashLength = new Uniform1f(this.program, 'u_DashLength');
+
+ numVertices = 4;
+ xy_NDC = new Float32Array(2 * this.numVertices);
+ xyBuffer_NDC: DynamicBuffer = newDynamicBuffer();
+
+ constructor(color?: Color) {
+ this._color = color;
}
-
- export function newBlendingBackgroundPainter( color : Color ) : Painter {
-
- var program = new Program( xyNdc_VERTSHADER, solid_FRAGSHADER );
- var u_Color = new UniformColor( program, 'u_Color' );
- var a_XyNdc = new Attribute( program, 'a_XyNdc' );
-
- var numVertices = 4;
- var xy_NDC = new Float32Array( 2*numVertices );
- var xyBuffer_NDC = newDynamicBuffer( );
-
- return function( gl : WebGLRenderingContext ) {
- if ( color.a >= 1 ) {
- gl.disable( GL.BLEND );
- }
- else {
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
- }
-
- program.use( gl );
- u_Color.setData( gl, color );
-
- xy_NDC[0] = -1;
- xy_NDC[1] = 1;
- xy_NDC[2] = -1;
- xy_NDC[3] = -1;
- xy_NDC[4] = 1;
- xy_NDC[5] = 1;
- xy_NDC[6] = 1;
- xy_NDC[7] = -1;
-
- xyBuffer_NDC.setData( xy_NDC );
- a_XyNdc.setDataAndEnable( gl, xyBuffer_NDC, 2, GL.FLOAT );
-
- gl.drawArrays( GL.TRIANGLE_STRIP, 0, numVertices );
-
- a_XyNdc.disable( gl );
- program.endUse( gl );
- };
+ get color(): Color {
+ return this._color;
}
-
- export class Background {
-
- private _color : Color;
- constructor( color? : Color ) {
+ set color(color: Color) {
+ if (!sameColor(this._color, color)) {
this._color = color;
}
-
- get color( ) : Color {
- return this._color;
- }
+ }
- set color( color : Color ) {
- if ( !sameColor( this._color, color ) ) {
- this._color = color;
- }
- }
-
- newPainter( ) {
- var background : Background = this;
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- if ( hasval( background.color ) ) {
- gl.clearColor( background.color.r, background.color.g, background.color.b, background.color.a );
- gl.clear( GL.COLOR_BUFFER_BIT );
- }
- };
- }
+ get dashPattern(): number {
+ return this._dashPattern;
}
-
- export function newBackgroundPainter( color : Color ) : Painter {
- return function( gl : WebGLRenderingContext ) {
- gl.clearColor( color.r, color.g, color.b, color.a );
- gl.clear( GL.COLOR_BUFFER_BIT );
- };
+ set dashPattern(pattern: number) {
+ this._dashPattern = pattern;
}
+ get dashLength(): number {
+ return this._dashLength;
+ }
- export function newTexturePainter( texture : Texture2D, xFrac : number, yFrac : number, options : TextureDrawOptions ) {
- var textureRenderer = new TextureRenderer( );
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- textureRenderer.begin( gl, viewport );
- textureRenderer.draw( gl, texture, xFrac, yFrac, options );
- textureRenderer.end( gl );
- };
+ set dashLength(length: number) {
+ this._dashLength = length;
}
+ newPainter() {
+ return (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) => {
+ if (this.color.a >= 1) {
+ gl.disable(GL.BLEND);
+ }
+ else {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+ }
- export function newSolidPane( color : Color ) : Pane {
- var pane = new Pane( null );
- pane.addPainter( newBackgroundPainter( color ) );
- return pane;
- }
+ this.program.use(gl);
+ this.u_Color.setData(gl, this.color);
+ this.u_DashPattern.setData(gl, this._dashPattern);
+ this.u_DashLength.setData(gl, this._dashLength);
+ this.u_yOffset.setData(gl, viewport.j + viewport.h);
+ this.xy_NDC[0] = -1;
+ this.xy_NDC[1] = 1;
+ this.xy_NDC[2] = -1;
+ this.xy_NDC[3] = -1;
+ this.xy_NDC[4] = 1;
+ this.xy_NDC[5] = 1;
+ this.xy_NDC[6] = 1;
+ this.xy_NDC[7] = -1;
- export function fitToTexture( texture : Texture2D ) {
- return function( parentPrefSize : Size ) {
- parentPrefSize.w = texture.w;
- parentPrefSize.h = texture.h;
- };
- }
+ this.xyBuffer_NDC.setData(this.xy_NDC);
+ this.a_XyNdc.setDataAndEnable(gl, this.xyBuffer_NDC, 2, GL.FLOAT);
+ gl.drawArrays(GL.TRIANGLE_STRIP, 0, this.numVertices);
- export function fixedSize( w : number, h : number ) {
- return function( parentPrefSize : Size ) {
- parentPrefSize.w = w;
- parentPrefSize.h = h;
+ this.a_XyNdc.disable(gl);
+ this.program.endUse(gl);
};
}
+}
+export class Background {
- /**
- * Takes (x,y) in NDC (Normalized Device Coords), in attribute a_XyNdc
- */
- export var xyNdc_VERTSHADER = concatLines(
- ' ',
- ' attribute vec2 a_XyNdc; ',
- ' ',
- ' void main( ) { ',
- ' gl_Position = vec4( a_XyNdc, 0.0, 1.0 ); ',
- ' } ',
- ' '
- );
-
-
- /**
- * Takes (x,y) as fractions of the viewport, in attribute a_XyFrac
- */
- export var xyFrac_VERTSHADER = concatLines(
- ' ',
- ' attribute vec2 a_XyFrac; ',
- ' ',
- ' void main( ) { ',
- ' gl_Position = vec4( ( -1.0 + 2.0*a_XyFrac ), 0.0, 1.0 ); ',
- ' } ',
- ' '
- );
-
-
- export var solid_FRAGSHADER = concatLines(
- ' ',
- ' precision lowp float; ',
- ' uniform vec4 u_Color; ',
- ' ',
- ' void main( ) { ',
- ' gl_FragColor = u_Color; ',
- ' } ',
- ' '
- );
-
-
- export var varyingColor_FRAGSHADER = concatLines(
- ' ',
- ' precision lowp float; ',
- ' varying vec4 v_Color; ',
- ' ',
- ' void main( ) { ',
- ' gl_FragColor = v_Color; ',
- ' } ',
- ' '
- );
-
-
- export var modelview_VERTSHADER = concatLines(
- ' uniform mat4 u_modelViewMatrix; ',
- ' attribute vec4 a_Position; ',
- ' ',
- ' void main( ) { ',
- ' gl_Position = u_modelViewMatrix * a_Position ; ',
- ' } ',
- ' '
- );
-
-
- export var nearestPixelCenter_GLSLFUNC = concatLines(
- ' ',
- ' float nearestPixelCenter( float frac, float pixelSize ) { ',
- ' return ( floor( frac*pixelSize + 1e-4 ) + 0.5 ) / pixelSize; ',
- ' } ',
- ' '
- );
-
-
- export enum Side {
- TOP, BOTTOM, RIGHT, LEFT
- }
+ private _color: Color;
+ constructor(color?: Color) {
+ this._color = color;
+ }
- /**
- * Converts viewport-fraction to NDC (Normalized Device Coords)
- */
- export function fracToNdc( frac : number ) : number {
- return -1 + 2*frac;
+ get color(): Color {
+ return this._color;
}
+ set color(color: Color) {
+ if (!sameColor(this._color, color)) {
+ this._color = color;
+ }
+ }
- export function nearestPixel( viewportFrac : number, viewportSize : number, imageAnchor : number, imageSize : number ) : number {
- var anchor = ( imageAnchor * imageSize ) % 1.0;
- return ( Math.floor( viewportFrac*viewportSize - anchor + 0.5 + 1e-4 ) + anchor ) / viewportSize;
+ newPainter() {
+ const background: Background = this;
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ if (hasval(background.color)) {
+ gl.clearColor(background.color.r, background.color.g, background.color.b, background.color.a);
+ gl.clear(GL.COLOR_BUFFER_BIT);
+ }
+ };
}
+}
- export function putQuadXys( xys : Float32Array, index : number, xLeft : number, xRight : number, yTop : number, yBottom : number ) : number {
- var n = index;
- n = putUpperLeftTriangleXys( xys, n, xLeft, xRight, yTop, yBottom );
- n = putLowerRightTriangleXys( xys, n, xLeft, xRight, yTop, yBottom );
- return n;
- }
+export function newBackgroundPainter(color: Color): Painter {
+ return function (gl: WebGLRenderingContext) {
+ gl.clearColor(color.r, color.g, color.b, color.a);
+ gl.clear(GL.COLOR_BUFFER_BIT);
+ };
+}
- export function putUpperLeftTriangleXys( xys : Float32Array, index : number, xLeft : number, xRight : number, yTop : number, yBottom : number ) : number {
- var n = index;
- xys[ n++ ] = xLeft; xys[ n++ ] = yTop;
- xys[ n++ ] = xRight; xys[ n++ ] = yTop;
- xys[ n++ ] = xLeft; xys[ n++ ] = yBottom;
- return n;
- }
+export function newTexturePainter(texture: Texture2D, xFrac: number, yFrac: number, options: TextureDrawOptions) {
+ const textureRenderer = new TextureRenderer();
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ textureRenderer.begin(gl, viewport);
+ textureRenderer.draw(gl, texture, xFrac, yFrac, options);
+ textureRenderer.end(gl);
+ };
+}
- export function putLowerRightTriangleXys( xys : Float32Array, index : number, xLeft : number, xRight : number, yTop : number, yBottom : number ) : number {
- var n = index;
- xys[ n++ ] = xLeft; xys[ n++ ] = yBottom;
- xys[ n++ ] = xRight; xys[ n++ ] = yTop;
- xys[ n++ ] = xRight; xys[ n++ ] = yBottom;
- return n;
- }
+export function newSolidPane(color: Color): Pane {
+ const pane = new Pane(null);
+ pane.addPainter(newBackgroundPainter(color));
+ return pane;
+}
- export function putUpperRightTriangleXys( xys : Float32Array, index : number, xLeft : number, xRight : number, yTop : number, yBottom : number ) : number {
- var n = index;
- xys[ n++ ] = xLeft; xys[ n++ ] = yTop;
- xys[ n++ ] = xRight; xys[ n++ ] = yTop;
- xys[ n++ ] = xRight; xys[ n++ ] = yBottom;
- return n;
- }
+export function fitToTexture(texture: Texture2D) {
+ return function (parentPrefSize: Size) {
+ parentPrefSize.w = texture.w;
+ parentPrefSize.h = texture.h;
+ };
+}
- export function putLowerLeftTriangleXys( xys : Float32Array, index : number, xLeft : number, xRight : number, yTop : number, yBottom : number ) : number {
- var n = index;
- xys[ n++ ] = xLeft; xys[ n++ ] = yBottom;
- xys[ n++ ] = xLeft; xys[ n++ ] = yTop;
- xys[ n++ ] = xRight; xys[ n++ ] = yBottom;
- return n;
- }
+export function fixedSize(w: number, h: number) {
+ return function (parentPrefSize: Size) {
+ parentPrefSize.w = w;
+ parentPrefSize.h = h;
+ };
+}
- export function putQuadRgbas( rgbas : Float32Array, index : number, color : Color ) : number {
- return putRgbas( rgbas, index, color, 6 );
- }
+/**
+ * Takes (x,y) in NDC (Normalized Device Coords), in attribute a_XyNdc
+ */
+export let xyNdc_VERTSHADER = concatLines(
+ ' ',
+ ' attribute vec2 a_XyNdc; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_Position = vec4( a_XyNdc, 0.0, 1.0 ); ',
+ ' } ',
+ ' '
+);
+
+
+/**
+ * Takes (x,y) as fractions of the viewport, in attribute a_XyFrac
+ */
+export let xyFrac_VERTSHADER = concatLines(
+ ' ',
+ ' attribute vec2 a_XyFrac; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_Position = vec4( ( -1.0 + 2.0*a_XyFrac ), 0.0, 1.0 ); ',
+ ' } ',
+ ' '
+);
+
+
+export let solid_FRAGSHADER = concatLines(
+ ' ',
+ ' precision lowp float; ',
+ ' uniform vec4 u_Color; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_FragColor = u_Color; ',
+ ' } ',
+ ' '
+);
+
+
+export let dash_FRAGSHADER = concatLines(
+ ' precision highp float; ',
+ ' uniform mat4 u_modelViewMatrix; ',
+ ' varying float v_Distance; ',
+ ' uniform float u_Dash; ',
+ ' uniform vec4 u_Color; ',
+ ' ',
+ ' void main( ) { ',
+ ' float v = floor(2.0 * fract(v_Distance * (u_Dash * (10.0 * u_modelViewMatrix[0][0])))); ',
+ ' if(v > 0.5) ',
+ ' discard; ',
+ ' else ',
+ ' gl_FragColor = u_Color; ',
+ ' } ',
+ ' '
+);
+
+export let dash2_FRAGSHADER = concatLines(
+ ' precision highp float; ',
+ ' uniform float u_DashLength; ',
+ ' uniform float u_DashPattern; ',
+ ' uniform float u_yOffset; ',
+ ' uniform vec4 u_Color; ',
+ ' const float maskLength = 16.0; ',
+ ' ',
+ ' void main( ) { ',
+ ' float dashPosition = fract((gl_FragCoord.y - u_yOffset) / u_DashLength); ',
+ ' float maskIndex = floor(dashPosition * maskLength); ',
+ ' float maskTest = floor(u_DashPattern / pow(2.0, maskIndex)); ',
+ ' ',
+ ' if(mod(maskTest, 2.0) < 1.0) ',
+ ' discard; ',
+ ' else ',
+ ' gl_FragColor = u_Color; ',
+ ' } ',
+ ' '
+);
+
+export let varyingColor_FRAGSHADER = concatLines(
+ ' ',
+ ' precision lowp float; ',
+ ' varying vec4 v_Color; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_FragColor = v_Color; ',
+ ' } ',
+ ' '
+);
+
+
+export let modelview_VERTSHADER = concatLines(
+ ' uniform mat4 u_modelViewMatrix; ',
+ ' attribute vec4 a_Position; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_Position = u_modelViewMatrix * a_Position ; ',
+ ' } ',
+ ' '
+);
+
+
+export let nearestPixelCenter_GLSLFUNC = concatLines(
+ ' ',
+ ' float nearestPixelCenter( float frac, float pixelSize ) { ',
+ ' return ( floor( frac*pixelSize + 1e-4 ) + 0.5 ) / pixelSize; ',
+ ' } ',
+ ' '
+);
+
+
+export enum Side {
+ TOP, BOTTOM, RIGHT, LEFT
+}
- export function putRgbas( rgbas : Float32Array, index : number, color : Color, count : number ) : number {
- var n = index;
- for ( var v = 0; v < count; v++ ) {
- rgbas[ n++ ] = color.r; rgbas[ n++ ] = color.g; rgbas[ n++ ] = color.b; rgbas[ n++ ] = color.a;
- }
- return n;
+/**
+ * Converts viewport-fraction to NDC (Normalized Device Coords)
+ */
+export function fracToNdc(frac: number): number {
+ return -1 + 2 * frac;
+}
+
+
+export function nearestPixel(viewportFrac: number, viewportSize: number, imageAnchor: number, imageSize: number): number {
+ const anchor = (imageAnchor * imageSize) % 1.0;
+ return (Math.floor(viewportFrac * viewportSize - anchor + 0.5 + 1e-4) + anchor) / viewportSize;
+}
+
+
+export function putQuadXys(xys: Float32Array, index: number, xLeft: number, xRight: number, yTop: number, yBottom: number): number {
+ let n = index;
+ n = putUpperLeftTriangleXys(xys, n, xLeft, xRight, yTop, yBottom);
+ n = putLowerRightTriangleXys(xys, n, xLeft, xRight, yTop, yBottom);
+ return n;
+}
+
+
+export function putUpperLeftTriangleXys(xys: Float32Array, index: number, xLeft: number, xRight: number, yTop: number, yBottom: number): number {
+ let n = index;
+ xys[n++] = xLeft; xys[n++] = yTop;
+ xys[n++] = xRight; xys[n++] = yTop;
+ xys[n++] = xLeft; xys[n++] = yBottom;
+ return n;
+}
+
+
+export function putLowerRightTriangleXys(xys: Float32Array, index: number, xLeft: number, xRight: number, yTop: number, yBottom: number): number {
+ let n = index;
+ xys[n++] = xLeft; xys[n++] = yBottom;
+ xys[n++] = xRight; xys[n++] = yTop;
+ xys[n++] = xRight; xys[n++] = yBottom;
+ return n;
+}
+
+
+export function putUpperRightTriangleXys(xys: Float32Array, index: number, xLeft: number, xRight: number, yTop: number, yBottom: number): number {
+ let n = index;
+ xys[n++] = xLeft; xys[n++] = yTop;
+ xys[n++] = xRight; xys[n++] = yTop;
+ xys[n++] = xRight; xys[n++] = yBottom;
+ return n;
+}
+
+
+export function putLowerLeftTriangleXys(xys: Float32Array, index: number, xLeft: number, xRight: number, yTop: number, yBottom: number): number {
+ let n = index;
+ xys[n++] = xLeft; xys[n++] = yBottom;
+ xys[n++] = xLeft; xys[n++] = yTop;
+ xys[n++] = xRight; xys[n++] = yBottom;
+ return n;
+}
+
+
+export function putQuadRgbas(rgbas: Float32Array, index: number, color: Color): number {
+ return putRgbas(rgbas, index, color, 6);
+}
+
+
+export function putRgbas(rgbas: Float32Array, index: number, color: Color, count: number): number {
+ let n = index;
+ for (let v = 0; v < count; v++) {
+ rgbas[n++] = color.r; rgbas[n++] = color.g; rgbas[n++] = color.b; rgbas[n++] = color.a;
}
+ return n;
+}
- export function clearSelection( ) {
- var selection = window.getSelection( );
- if ( selection ) {
- if ( selection[ 'removeAllRanges' ] ) {
- selection[ 'removeAllRanges' ]( );
- }
- else if ( selection[ 'empty' ] ) {
- selection[ 'empty' ]( );
- }
+export function clearSelection() {
+ const selection = window.getSelection();
+ if (selection) {
+ if (selection['removeAllRanges']) {
+ selection['removeAllRanges']();
+ }
+ else if (selection['empty']) {
+ selection['empty']();
}
}
+}
- export class SimpleModel {
- private _value : V;
- private _changed : Notification;
+export class SimpleModel {
+ private _value: V;
+ private _changed: Notification;
- constructor( value : V = null ) {
- this._value = value;
- this._changed = new Notification( );
- }
+ constructor(value: V = null) {
+ this._value = value;
+ this._changed = new Notification();
+ }
- get value( ) : V { return this._value; }
- get changed( ) : Notification { return this._changed; }
+ get value(): V { return this._value; }
+ get changed(): Notification { return this._changed; }
- set value( value : V ) {
- if ( value !== this._value ) {
- this._value = value;
- this._changed.fire( );
- }
+ set value(value: V) {
+ if (value !== this._value) {
+ this._value = value;
+ this._changed.fire();
}
}
+}
- export class XyModel {
- private _x : number;
- private _y : number;
- private _changed : Notification;
+export class XyModel {
+ private _x: number;
+ private _y: number;
+ private _changed: Notification;
- constructor( x? : number, y? : number ) {
- this._x = x;
- this._y = y;
- this._changed = new Notification( );
- }
+ constructor(x?: number, y?: number) {
+ this._x = x;
+ this._y = y;
+ this._changed = new Notification();
+ }
- get x( ) : number { return this._x; }
- get y( ) : number { return this._y; }
- get changed( ) : Notification { return this._changed; }
+ get x(): number { return this._x; }
+ get y(): number { return this._y; }
+ get changed(): Notification { return this._changed; }
- set x( x : number ) {
- if ( x !== this._x ) {
- this._x = x;
- this._changed.fire( );
- }
+ set x(x: number) {
+ if (x !== this._x) {
+ this._x = x;
+ this._changed.fire();
}
+ }
- set y( y : number ) {
- if ( y !== this._y ) {
- this._y = y;
- this._changed.fire( );
- }
+ set y(y: number) {
+ if (y !== this._y) {
+ this._y = y;
+ this._changed.fire();
}
+ }
- setXy( x : number, y : number ) {
- if ( x !== this._x || y !== this._y ) {
- this._x = x;
- this._y = y;
- this._changed.fire( );
- }
+ setXy(x: number, y: number) {
+ if (x !== this._x || y !== this._y) {
+ this._x = x;
+ this._y = y;
+ this._changed.fire();
}
}
-
-
}
diff --git a/src/webglimpse/painter/border_painter.ts b/src/webglimpse/painter/border_painter.ts
index a1e2d9d..6c484dd 100644
--- a/src/webglimpse/painter/border_painter.ts
+++ b/src/webglimpse/painter/border_painter.ts
@@ -27,131 +27,156 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
-
- export interface BorderOptions {
- drawTop? : boolean;
- drawLeft? : boolean;
- drawRight? : boolean;
- drawBottom? : boolean;
- thickness? : number;
+import { Color } from '../color';
+import { Painter } from '../core';
+import { Program, UniformColor, Attribute } from '../shader';
+import { xyNdc_VERTSHADER, solid_FRAGSHADER, putQuadXys, fracToNdc } from '../misc';
+import { newDynamicBuffer } from '../buffer';
+import { BoundsUnmodifiable } from '../bounds';
+import { GL, hasval } from '../util/util';
+
+
+export interface BorderOptions {
+ drawTop?: boolean;
+ drawLeft?: boolean;
+ drawRight?: boolean;
+ drawBottom?: boolean;
+ thickness?: number;
+}
+
+
+export function newBorderPainter(color: Color, options?: BorderOptions): Painter {
+ if (!hasval(options)) {
+ options = {};
}
-
-
- export function newBorderPainter( color : Color, options? : BorderOptions ) : Painter {
- if ( !hasval( options ) ) options = { };
- if ( !hasval( options.drawTop ) ) options.drawTop = true;
- if ( !hasval( options.drawLeft ) ) options.drawLeft = true;
- if ( !hasval( options.drawRight ) ) options.drawRight = true;
- if ( !hasval( options.drawBottom ) ) options.drawBottom = true;
- if ( !hasval( options.thickness ) ) options.thickness = 1;
-
- var simple = ( options.thickness === 1 && color.a >= 1 );
- return ( simple ? newSimpleBorderPainter( color, options ) : newFullBorderPainter( color, options ) );
+ if (!hasval(options.drawTop)) {
+ options.drawTop = true;
}
-
-
- function newFullBorderPainter( color : Color, options : BorderOptions ) : Painter {
- var drawTop = options.drawTop;
- var drawLeft = options.drawLeft;
- var drawRight = options.drawRight;
- var drawBottom = options.drawBottom;
- var thickness = options.thickness;
-
- var program = new Program( xyNdc_VERTSHADER, solid_FRAGSHADER );
- var u_Color = new UniformColor( program, 'u_Color' );
- var a_XyNdc = new Attribute( program, 'a_XyNdc' );
-
- var numVertices = ( drawTop ? 6 : 0 ) + ( drawLeft ? 6 : 0 ) + ( drawRight ? 6 : 0 ) + ( drawBottom ? 6 : 0 );
- var xy_NDC = new Float32Array( 2*numVertices );
- var xyBuffer_NDC = newDynamicBuffer( );
-
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- if ( color.a >= 1 ) {
- gl.disable( GL.BLEND );
- }
- else {
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
- }
-
- program.use( gl );
- u_Color.setData( gl, color );
-
- var w_NDC = 2*thickness / viewport.w;
- var h_NDC = 2*thickness / viewport.h;
- var index = 0;
- if ( drawTop ) index = putQuadXys( xy_NDC, index, -1, ( drawRight ? +1-w_NDC : +1 ), +1, +1-h_NDC );
- if ( drawRight ) index = putQuadXys( xy_NDC, index, +1-w_NDC, +1, +1, ( drawBottom ? -1+h_NDC : -1 ) );
- if ( drawBottom ) index = putQuadXys( xy_NDC, index, ( drawLeft ? -1+w_NDC : -1 ), +1, -1+h_NDC, -1 );
- if ( drawLeft ) index = putQuadXys( xy_NDC, index, -1, -1+w_NDC, ( drawTop ? +1-h_NDC : +1 ), -1 );
-
- xyBuffer_NDC.setData( xy_NDC );
- a_XyNdc.setDataAndEnable( gl, xyBuffer_NDC, 2, GL.FLOAT );
-
- gl.drawArrays( GL.TRIANGLES, 0, numVertices );
-
- a_XyNdc.disable( gl );
- program.endUse( gl );
- };
+ if (!hasval(options.drawLeft)) {
+ options.drawLeft = true;
}
-
-
- function newSimpleBorderPainter( color : Color, options : BorderOptions ) : Painter {
- var drawTop = options.drawTop;
- var drawLeft = options.drawLeft;
- var drawRight = options.drawRight;
- var drawBottom = options.drawBottom;
-
- var program = new Program( xyNdc_VERTSHADER, solid_FRAGSHADER );
- var u_Color = new UniformColor( program, 'u_Color' );
- var a_XyNdc = new Attribute( program, 'a_XyNdc' );
-
- var numVertices = ( drawTop ? 2 : 0 ) + ( drawLeft ? 2 : 0 ) + ( drawRight ? 2 : 0 ) + ( drawBottom ? 2 : 0 );
- var xy_NDC = new Float32Array( 2*numVertices );
- var xyBuffer_NDC = newDynamicBuffer( );
-
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- gl.disable( GL.BLEND );
-
- program.use( gl );
- u_Color.setData( gl, color );
-
- var left_NDC = fracToNdc( 0.5 / viewport.w );
- var bottom_NDC = fracToNdc( 0.5 / viewport.h );
- var right_NDC = fracToNdc( ( viewport.w - 0.5 ) / viewport.w );
- var top_NDC = fracToNdc( ( viewport.h - 0.5 ) / viewport.h );
-
- var n = 0;
- if ( drawTop ) {
- xy_NDC[ n++ ] = -1; xy_NDC[ n++ ] = top_NDC;
- xy_NDC[ n++ ] = +1; xy_NDC[ n++ ] = top_NDC;
- }
- if ( drawRight ) {
- xy_NDC[ n++ ] = right_NDC; xy_NDC[ n++ ] = +1;
- xy_NDC[ n++ ] = right_NDC; xy_NDC[ n++ ] = -1;
- }
- if ( drawBottom ) {
- xy_NDC[ n++ ] = +1; xy_NDC[ n++ ] = bottom_NDC;
- xy_NDC[ n++ ] = -1; xy_NDC[ n++ ] = bottom_NDC;
- }
- if ( drawLeft ) {
- xy_NDC[ n++ ] = left_NDC; xy_NDC[ n++ ] = -1;
- xy_NDC[ n++ ] = left_NDC; xy_NDC[ n++ ] = +1;
- }
-
- xyBuffer_NDC.setData( xy_NDC );
- a_XyNdc.setDataAndEnable( gl, xyBuffer_NDC, 2, GL.FLOAT );
-
- // IE does not support lineWidths other than 1, so make sure all browsers use lineWidth of 1
- gl.lineWidth( 1 );
- gl.drawArrays( GL.LINES, 0, numVertices );
-
- a_XyNdc.disable( gl );
- program.endUse( gl );
- };
+ if (!hasval(options.drawRight)) {
+ options.drawRight = true;
}
+ if (!hasval(options.drawBottom)) {
+ options.drawBottom = true;
+ }
+ if (!hasval(options.thickness)) {
+ options.thickness = 1;
+ }
+
+ const simple = (options.thickness === 1 && color.a >= 1);
+ return (simple ? newSimpleBorderPainter(color, options) : newFullBorderPainter(color, options));
+}
+
+
+function newFullBorderPainter(color: Color, options: BorderOptions): Painter {
+ const drawTop = options.drawTop;
+ const drawLeft = options.drawLeft;
+ const drawRight = options.drawRight;
+ const drawBottom = options.drawBottom;
+ const thickness = options.thickness;
+
+ const program = new Program(xyNdc_VERTSHADER, solid_FRAGSHADER);
+ const u_Color = new UniformColor(program, 'u_Color');
+ const a_XyNdc = new Attribute(program, 'a_XyNdc');
+
+ const numVertices = (drawTop ? 6 : 0) + (drawLeft ? 6 : 0) + (drawRight ? 6 : 0) + (drawBottom ? 6 : 0);
+ const xy_NDC = new Float32Array(2 * numVertices);
+ const xyBuffer_NDC = newDynamicBuffer();
+
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ if (color.a >= 1) {
+ gl.disable(GL.BLEND);
+ }
+ else {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+ }
+
+ program.use(gl);
+ u_Color.setData(gl, color);
+
+ const w_NDC = 2 * thickness / viewport.w;
+ const h_NDC = 2 * thickness / viewport.h;
+ let index = 0;
+ if (drawTop) {
+ index = putQuadXys(xy_NDC, index, -1, (drawRight ? +1 - w_NDC : +1), +1, +1 - h_NDC);
+ }
+ if (drawRight) {
+ index = putQuadXys(xy_NDC, index, +1 - w_NDC, +1, +1, (drawBottom ? -1 + h_NDC : -1));
+ }
+ if (drawBottom) {
+ index = putQuadXys(xy_NDC, index, (drawLeft ? -1 + w_NDC : -1), +1, -1 + h_NDC, -1);
+ }
+ if (drawLeft) {
+ index = putQuadXys(xy_NDC, index, -1, -1 + w_NDC, (drawTop ? +1 - h_NDC : +1), -1);
+ }
+
+ xyBuffer_NDC.setData(xy_NDC);
+ a_XyNdc.setDataAndEnable(gl, xyBuffer_NDC, 2, GL.FLOAT);
+
+ gl.drawArrays(GL.TRIANGLES, 0, numVertices);
+
+ a_XyNdc.disable(gl);
+ program.endUse(gl);
+ };
+}
+
+
+function newSimpleBorderPainter(color: Color, options: BorderOptions): Painter {
+ const drawTop = options.drawTop;
+ const drawLeft = options.drawLeft;
+ const drawRight = options.drawRight;
+ const drawBottom = options.drawBottom;
+
+ const program = new Program(xyNdc_VERTSHADER, solid_FRAGSHADER);
+ const u_Color = new UniformColor(program, 'u_Color');
+ const a_XyNdc = new Attribute(program, 'a_XyNdc');
+
+ const numVertices = (drawTop ? 2 : 0) + (drawLeft ? 2 : 0) + (drawRight ? 2 : 0) + (drawBottom ? 2 : 0);
+ const xy_NDC = new Float32Array(2 * numVertices);
+ const xyBuffer_NDC = newDynamicBuffer();
+
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ gl.disable(GL.BLEND);
+
+ program.use(gl);
+ u_Color.setData(gl, color);
+
+ const left_NDC = fracToNdc(0.5 / viewport.w);
+ const bottom_NDC = fracToNdc(0.5 / viewport.h);
+ const right_NDC = fracToNdc((viewport.w - 0.5) / viewport.w);
+ const top_NDC = fracToNdc((viewport.h - 0.5) / viewport.h);
+
+ let n = 0;
+ if (drawTop) {
+ xy_NDC[n++] = -1; xy_NDC[n++] = top_NDC;
+ xy_NDC[n++] = +1; xy_NDC[n++] = top_NDC;
+ }
+ if (drawRight) {
+ xy_NDC[n++] = right_NDC; xy_NDC[n++] = +1;
+ xy_NDC[n++] = right_NDC; xy_NDC[n++] = -1;
+ }
+ if (drawBottom) {
+ xy_NDC[n++] = +1; xy_NDC[n++] = bottom_NDC;
+ xy_NDC[n++] = -1; xy_NDC[n++] = bottom_NDC;
+ }
+ if (drawLeft) {
+ xy_NDC[n++] = left_NDC; xy_NDC[n++] = -1;
+ xy_NDC[n++] = left_NDC; xy_NDC[n++] = +1;
+ }
+
+ xyBuffer_NDC.setData(xy_NDC);
+ a_XyNdc.setDataAndEnable(gl, xyBuffer_NDC, 2, GL.FLOAT);
+
+ // IE does not support lineWidths other than 1, so make sure all browsers use lineWidth of 1
+ gl.lineWidth(1);
+ gl.drawArrays(GL.LINES, 0, numVertices);
+
+ a_XyNdc.disable(gl);
+ program.endUse(gl);
+ };
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/plot/axis.ts b/src/webglimpse/plot/axis.ts
index 6b78121..0472792 100644
--- a/src/webglimpse/plot/axis.ts
+++ b/src/webglimpse/plot/axis.ts
@@ -27,213 +27,258 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Notification, Listener } from '../util/notification';
+import { log10, hasval } from '../util/util';
+import { xFrac, yFrac, Pane, PointerEvent, isLeftMouseDown } from '../core';
+
+
+export class Axis1D {
+ private _vMin: number;
+ private _vMax: number;
+ private _vMinLimit: number;
+ private _vMaxLimit: number;
+ private _limitsChanged = new Notification();
+
+ constructor(vMin: number, vMax: number, vMinLimit = -Infinity, vMaxLimit = Infinity) {
+ this._vMin = Math.max(vMin, vMinLimit);
+ this._vMax = Math.min(vMax, vMaxLimit);
+ this._vMinLimit = vMinLimit;
+ this._vMaxLimit = vMaxLimit;
+ }
+ get vMin(): number {
+ return this._vMin;
+ }
- export class Axis1D {
- private _vMin : number;
- private _vMax : number;
- private _limitsChanged = new Notification( );
+ get vMax(): number {
+ return this._vMax;
+ }
- constructor( vMin : number, vMax : number ) {
- this._vMin = vMin;
- this._vMax = vMax;
- }
+ get vMinLimit(): number {
+ return this._vMinLimit;
+ }
- get vMin( ) : number {
- return this._vMin;
- }
+ get vMaxLimit(): number {
+ return this._vMaxLimit;
+ }
- get vMax( ) : number {
- return this._vMax;
- }
+ get limitsChanged(): Notification {
+ return this._limitsChanged;
+ }
- get limitsChanged( ) : Notification {
- return this._limitsChanged;
- }
+ set vMin(vMin: number) {
+ this._vMin = Math.max(vMin, this._vMinLimit);
+ this._limitsChanged.fire();
+ }
- set vMin( vMin : number ) {
- this._vMin = vMin;
- this._limitsChanged.fire( );
- }
+ set vMax(vMax: number) {
+ this._vMax = Math.min(vMax, this._vMaxLimit);
+ this._limitsChanged.fire();
+ }
- set vMax( vMax : number ) {
- this._vMax = vMax;
- this._limitsChanged.fire( );
- }
+ set vMinLimit(vMinLimit: number) {
+ this._vMinLimit = vMinLimit;
+ this._vMin = Math.max(this._vMin, vMinLimit);
+ this._vMax = Math.max(this._vMax, vMinLimit);
+ this._limitsChanged.fire();
+ }
- setVRange( vMin : number, vMax : number ) {
- this._vMin = vMin;
- this._vMax = vMax;
- this._limitsChanged.fire( );
- }
+ set vMaxLimit(vMaxLimit: number) {
+ this._vMaxLimit = vMaxLimit;
+ this._vMin = Math.min(this._vMin, vMaxLimit);
+ this._vMax = Math.min(this._vMax, vMaxLimit);
+ this._limitsChanged.fire();
+ }
- get vSize( ) : number {
- return ( this._vMax - this._vMin );
- }
+ setVRange(vMin: number, vMax: number) {
+ this._vMin = Math.max(vMin, this._vMinLimit);
+ this._vMax = Math.min(vMax, this._vMaxLimit);
+ this._limitsChanged.fire();
+ }
- vAtFrac( vFrac : number ) : number {
- return ( this._vMin + vFrac*( this._vMax - this._vMin ) );
- }
+ get vSize(): number {
+ return (this._vMax - this._vMin);
+ }
- vFrac( v : number ) : number {
- return ( v - this._vMin ) / ( this._vMax - this._vMin );
- }
+ vAtFrac(vFrac: number): number {
+ return (this._vMin + vFrac * (this._vMax - this._vMin));
+ }
+
+ vFrac(v: number): number {
+ return (v - this._vMin) / (this._vMax - this._vMin);
+ }
- pan( vAmount : number ) {
+ pan(vAmount: number) {
+ if (vAmount + this._vMin < this._vMinLimit) {
+ this._vMax = this._vMinLimit + this.vSize;
+ this._vMin = this._vMinLimit;
+ } else if (vAmount + this._vMax > this._vMaxLimit) {
+ this._vMin = this._vMaxLimit - this.vSize;
+ this._vMax = this._vMaxLimit;
+ } else {
this._vMin += vAmount;
this._vMax += vAmount;
- this._limitsChanged.fire( );
- }
-
- zoom( factor : number, vAnchor : number ) {
- this._vMin = vAnchor - factor*( vAnchor - this._vMin );
- this._vMax = vAnchor + factor*( this._vMax - vAnchor );
- this._limitsChanged.fire( );
}
+ this._limitsChanged.fire();
}
+ zoom(factor: number, vAnchor: number) {
+ const newVMin = vAnchor - factor * (vAnchor - this._vMin);
+ const newVMax = vAnchor + factor * (this._vMax - vAnchor);
+ this._vMin = Math.max(newVMin, this._vMinLimit);
+ this._vMax = Math.min(newVMax, this._vMaxLimit);
+ this._limitsChanged.fire();
+ }
+}
- export function getTickInterval( axis : Axis1D, approxNumTicks : number ) : number {
- var vMin = Math.min( axis.vMin, axis.vMax );
- var vMax = Math.max( axis.vMin, axis.vMax );
- var approxTickInterval = ( vMax - vMin ) / approxNumTicks;
- var prelimTickInterval = Math.pow( 10, Math.round( log10( approxTickInterval ) ) );
- var prelimNumTicks = ( vMax - vMin ) / prelimTickInterval;
- if ( prelimNumTicks >= 5 * approxNumTicks ) return ( prelimTickInterval * 5 );
- if ( prelimNumTicks >= 2 * approxNumTicks ) return ( prelimTickInterval * 2 );
+export function getTickInterval(axis: Axis1D, approxNumTicks: number): number {
+ const vMin = Math.min(axis.vMin, axis.vMax);
+ const vMax = Math.max(axis.vMin, axis.vMax);
+ const approxTickInterval = (vMax - vMin) / approxNumTicks;
+ const prelimTickInterval = Math.pow(10, Math.round(log10(approxTickInterval)));
+ const prelimNumTicks = (vMax - vMin) / prelimTickInterval;
- if ( 5 * prelimNumTicks <= approxNumTicks ) return ( prelimTickInterval / 5 );
- if ( 2 * prelimNumTicks <= approxNumTicks ) return ( prelimTickInterval / 2 );
+ if (prelimNumTicks >= 5 * approxNumTicks) {
+ return (prelimTickInterval * 5);
+ }
+ if (prelimNumTicks >= 2 * approxNumTicks) {
+ return (prelimTickInterval * 2);
+ }
- return prelimTickInterval;
+ if (5 * prelimNumTicks <= approxNumTicks) {
+ return (prelimTickInterval / 5);
+ }
+ if (2 * prelimNumTicks <= approxNumTicks) {
+ return (prelimTickInterval / 2);
}
+ return prelimTickInterval;
+}
- export function getTickCount( axis : Axis1D, tickInterval : number ) : number {
- return Math.ceil( Math.abs( axis.vSize ) / tickInterval ) + 1;
- }
+export function getTickCount(axis: Axis1D, tickInterval: number): number {
+ return Math.ceil(Math.abs(axis.vSize) / tickInterval) + 1;
+}
- export function getTickPositions( axis : Axis1D, tickInterval : number, tickCount : number, result : Float32Array ) {
- var vMin = Math.min( axis.vMin, axis.vMax );
- var vMax = Math.max( axis.vMin, axis.vMax );
- var minTickNumber = Math.floor( vMin / tickInterval );
+export function getTickPositions(axis: Axis1D, tickInterval: number, tickCount: number, result: Float32Array) {
+ const vMin = Math.min(axis.vMin, axis.vMax);
+ const vMax = Math.max(axis.vMin, axis.vMax);
- for ( var i = 0; i < tickCount; i++ ) {
- result[ i ] = ( minTickNumber + i ) * tickInterval;
- }
+ const minTickNumber = Math.floor(vMin / tickInterval);
+
+ for (let i = 0; i < tickCount; i++) {
+ result[i] = (minTickNumber + i) * tickInterval;
+ }
- if ( axis.vMin > axis.vMax ) {
- // XXX: Need floor() on tickCount/2?
- for ( var i = 0; i < tickCount/2; i++ ) {
- var temp = result[ i ];
- result[ i ] = result[ tickCount-1 - i ];
- result[ tickCount-1 - i ] = temp;
- }
+ if (axis.vMin > axis.vMax) {
+ // XXX: Need floor() on tickCount/2?
+ for (let i = 0; i < tickCount / 2; i++) {
+ const temp = result[i];
+ result[i] = result[tickCount - 1 - i];
+ result[tickCount - 1 - i] = temp;
}
}
+}
- export class Axis2D {
- private _xAxis : Axis1D;
- private _yAxis : Axis1D;
+export class Axis2D {
+ private _xAxis: Axis1D;
+ private _yAxis: Axis1D;
- get xAxis( ) : Axis1D { return this._xAxis; }
- get xMin( ) : number { return this._xAxis.vMin; }
- get xMax( ) : number { return this._xAxis.vMax; }
- xAtFrac( xFrac : number ) : number { return this._xAxis.vAtFrac( xFrac ); }
+ get xAxis(): Axis1D { return this._xAxis; }
+ get xMin(): number { return this._xAxis.vMin; }
+ get xMax(): number { return this._xAxis.vMax; }
+ xAtFrac(xFrac: number): number { return this._xAxis.vAtFrac(xFrac); }
- get yAxis( ) : Axis1D { return this._yAxis; }
- get yMin( ) : number { return this._yAxis.vMin; }
- get yMax( ) : number { return this._yAxis.vMax; }
- yAtFrac( yFrac : number ) : number { return this._yAxis.vAtFrac( yFrac ); }
+ get yAxis(): Axis1D { return this._yAxis; }
+ get yMin(): number { return this._yAxis.vMin; }
+ get yMax(): number { return this._yAxis.vMax; }
+ yAtFrac(yFrac: number): number { return this._yAxis.vAtFrac(yFrac); }
- constructor( xAxis : Axis1D, yAxis : Axis1D ) {
- this._xAxis = xAxis;
- this._yAxis = yAxis;
- }
+ constructor(xAxis: Axis1D, yAxis: Axis1D) {
+ this._xAxis = xAxis;
+ this._yAxis = yAxis;
+ }
- onLimitsChanged( listener : Listener ) {
- this._xAxis.limitsChanged.on( listener );
- this._yAxis.limitsChanged.on( listener );
- }
+ onLimitsChanged(listener: Listener) {
+ this._xAxis.limitsChanged.on(listener);
+ this._yAxis.limitsChanged.on(listener);
+ }
- pan( xAmount : number, yAmount : number ) {
- this._xAxis.pan( xAmount );
- this._yAxis.pan( yAmount );
- }
+ pan(xAmount: number, yAmount: number) {
+ this._xAxis.pan(xAmount);
+ this._yAxis.pan(yAmount);
+ }
- zoom( factor : number, xAnchor : number, yAnchor : number ) {
- this._xAxis.zoom( factor, xAnchor );
- this._yAxis.zoom( factor, yAnchor );
- }
+ zoom(factor: number, xAnchor: number, yAnchor: number) {
+ this._xAxis.zoom(factor, xAnchor);
+ this._yAxis.zoom(factor, yAnchor);
}
+}
- export function newAxis2D( xMin : number, xMax : number, yMin : number, yMax : number ) : Axis2D {
- return new Axis2D( new Axis1D( xMin, xMax ), new Axis1D( yMin, yMax ) );
- }
+export function newAxis2D(xMin: number, xMax: number, yMin: number, yMax: number): Axis2D {
+ return new Axis2D(new Axis1D(xMin, xMax), new Axis1D(yMin, yMax));
+}
- // XXX: Would be nice if this could be a const
- export var axisZoomStep = 1.12;
+// XXX: Would be nice if this could be a const
+export let axisZoomStep = 1.12;
- export function attachAxisMouseListeners1D( pane : Pane, axis : Axis1D, isVertical : boolean ) {
- var vGrab : number = null;
+export function attachAxisMouseListeners1D(pane: Pane, axis: Axis1D, isVertical: boolean) {
+ let vGrab: number = null;
- pane.mouseDown.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) && !hasval( vGrab ) ) {
- vGrab = axis.vAtFrac( isVertical ? yFrac( ev ) : xFrac( ev ) );
- }
- } );
+ pane.mouseDown.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent) && !hasval(vGrab)) {
+ vGrab = axis.vAtFrac(isVertical ? yFrac(ev) : xFrac(ev));
+ }
+ });
- pane.mouseMove.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) && hasval( vGrab ) ) {
- axis.pan( vGrab - axis.vAtFrac( isVertical ? yFrac( ev ) : xFrac( ev ) ) );
- }
- } );
+ pane.mouseMove.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent) && hasval(vGrab)) {
+ axis.pan(vGrab - axis.vAtFrac(isVertical ? yFrac(ev) : xFrac(ev)));
+ }
+ });
- pane.mouseUp.on( function( ev : PointerEvent ) {
- vGrab = null;
- } );
+ pane.mouseUp.on(function (ev: PointerEvent) {
+ vGrab = null;
+ });
- pane.mouseWheel.on( function( ev : PointerEvent ) {
- var zoomFactor = Math.pow( axisZoomStep, ev.wheelSteps );
- axis.zoom( zoomFactor, axis.vAtFrac( isVertical ? yFrac( ev ) : xFrac( ev ) ) );
- } );
- }
+ pane.mouseWheel.on(function (ev: PointerEvent) {
+ const zoomFactor = Math.pow(axisZoomStep, ev.wheelSteps);
+ axis.zoom(zoomFactor, axis.vAtFrac(isVertical ? yFrac(ev) : xFrac(ev)));
+ });
+}
- export function attachAxisMouseListeners2D( pane : Pane, axis : Axis2D ) {
- var xGrab : number = null;
- var yGrab : number = null;
+export function attachAxisMouseListeners2D(pane: Pane, axis: Axis2D) {
+ let xGrab: number = null;
+ let yGrab: number = null;
- pane.mouseDown.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) && !hasval( xGrab ) ) {
- xGrab = axis.xAtFrac( xFrac( ev ) );
- yGrab = axis.yAtFrac( yFrac( ev ) );
- }
- } );
+ pane.mouseDown.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent) && !hasval(xGrab)) {
+ xGrab = axis.xAtFrac(xFrac(ev));
+ yGrab = axis.yAtFrac(yFrac(ev));
+ }
+ });
- pane.mouseMove.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) && hasval( xGrab ) ) {
- axis.pan( xGrab - axis.xAtFrac( xFrac( ev ) ), yGrab - axis.yAtFrac( yFrac( ev ) ) );
- }
- } );
+ pane.mouseMove.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent) && hasval(xGrab)) {
+ axis.pan(xGrab - axis.xAtFrac(xFrac(ev)), yGrab - axis.yAtFrac(yFrac(ev)));
+ }
+ });
- pane.mouseUp.on( function( ev : PointerEvent ) {
- xGrab = null;
- yGrab = null;
- } );
+ pane.mouseUp.on(function (ev: PointerEvent) {
+ xGrab = null;
+ yGrab = null;
+ });
- pane.mouseWheel.on( function( ev : PointerEvent ) {
- var zoomFactor = Math.pow( axisZoomStep, ev.wheelSteps );
- axis.zoom( zoomFactor, axis.xAtFrac( xFrac( ev ) ), axis.yAtFrac( yFrac( ev ) ) );
- } );
- }
+ pane.mouseWheel.on(function (ev: PointerEvent) {
+ const zoomFactor = Math.pow(axisZoomStep, ev.wheelSteps);
+ axis.zoom(zoomFactor, axis.xAtFrac(xFrac(ev)), axis.yAtFrac(yFrac(ev)));
+ });
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/plot/edge_axis_painter.ts b/src/webglimpse/plot/edge_axis_painter.ts
index 08f4675..8724d37 100644
--- a/src/webglimpse/plot/edge_axis_painter.ts
+++ b/src/webglimpse/plot/edge_axis_painter.ts
@@ -27,389 +27,411 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
-
- export function edgeMarks_VERTSHADER( labelSide : Side ) {
- // The shader uses 'a' for the along-axis coord, and 'b' for the across-axis coord
- var horizontal = ( labelSide === Side.TOP || labelSide === Side.BOTTOM );
- var bFlip = ( labelSide === Side.LEFT || labelSide === Side.BOTTOM );
- return concatLines(
- nearestPixelCenter_GLSLFUNC,
- ' ',
- ' uniform float u_VMin; ',
- ' uniform float u_VSize; ',
- ' uniform vec2 u_ViewportSize; ',
- ' uniform float u_MarkSize; ',
- ' ',
- ' attribute vec2 a_VCoord; ',
- ' ',
- ' void main( ) { ',
- ' float aViewportSize = ' + ( horizontal ? 'u_ViewportSize.x' : 'u_ViewportSize.y' ) + '; ',
- ' float aFrac = nearestPixelCenter( ( a_VCoord.x - u_VMin ) / u_VSize, aViewportSize ); ',
- ' float a = -1.0 + 2.0*( aFrac ); ',
- ' ',
- ' float bViewportSize = ' + ( horizontal ? 'u_ViewportSize.y' : 'u_ViewportSize.x' ) + '; ',
- ' float bFrac = ( a_VCoord.y * u_MarkSize ) / bViewportSize; ',
- ' float b = ' + ( bFlip ? '-' : '' ) + '( -1.0 + 2.0*( bFrac ) ); ',
- ' ',
- ' gl_Position = vec4( ' + ( horizontal ? 'a,b' : 'b,a' ) + ', 0.0, 1.0 ); ',
- ' } ',
- ' '
- );
- }
-
- export var gradient_FRAGSHADER = concatLines(
- ' ',
- ' precision highp float; ',
- ' uniform sampler2D u_colorTex; ',
- ' ',
- ' varying vec2 v_texCoord; ',
- ' ',
- ' void main( ) { ',
- ' vec4 color = texture2D( u_colorTex, v_texCoord ); ',
- ' gl_FragColor = color; ',
- ' gl_FragColor.a = 1.0; ',
- ' } '
+import { Side, nearestPixelCenter_GLSLFUNC, modelview_VERTSHADER, solid_FRAGSHADER } from '../misc';
+import { concatLines, ensureCapacityFloat32, GL, order, hasval, clamp } from '../util/util';
+import { Axis1D, getTickInterval, getTickCount, getTickPositions } from './axis';
+import { Color, black } from '../color';
+import { Gradient, getGradientTexture } from '../gradient';
+import { Painter } from '../core';
+import { Program, UniformMatrix4f, UniformSampler2D, Attribute, UniformColor, Uniform1f, Uniform2f } from '../shader';
+import { heatmap_VERTSHADER } from './heatmap_painter';
+import { newDynamicBuffer } from '../buffer';
+import { Cache } from '../util/cache';
+import { TextTexture2D, newTextTextureCache } from '../text';
+import { TextureRenderer, TextureDrawOptions, Texture } from '../texture';
+import { BoundsUnmodifiable } from '../bounds';
+import { glOrthoViewport } from '../matrix';
+
+
+export function edgeMarks_VERTSHADER(labelSide: Side) {
+ // The shader uses 'a' for the along-axis coord, and 'b' for the across-axis coord
+ const horizontal = (labelSide === Side.TOP || labelSide === Side.BOTTOM);
+ const bFlip = (labelSide === Side.LEFT || labelSide === Side.BOTTOM);
+ return concatLines(
+ nearestPixelCenter_GLSLFUNC,
+ ' ',
+ ' uniform float u_VMin; ',
+ ' uniform float u_VSize; ',
+ ' uniform vec2 u_ViewportSize; ',
+ ' uniform float u_MarkSize; ',
+ ' ',
+ ' attribute vec2 a_VCoord; ',
+ ' ',
+ ' void main( ) { ',
+ ' float aViewportSize = ' + (horizontal ? 'u_ViewportSize.x' : 'u_ViewportSize.y') + '; ',
+ ' float aFrac = nearestPixelCenter( ( a_VCoord.x - u_VMin ) / u_VSize, aViewportSize ); ',
+ ' float a = -1.0 + 2.0*( aFrac ); ',
+ ' ',
+ ' float bViewportSize = ' + (horizontal ? 'u_ViewportSize.y' : 'u_ViewportSize.x') + '; ',
+ ' float bFrac = ( a_VCoord.y * u_MarkSize ) / bViewportSize; ',
+ ' float b = ' + (bFlip ? '-' : '') + '( -1.0 + 2.0*( bFrac ) ); ',
+ ' ',
+ ' gl_Position = vec4( ' + (horizontal ? 'a,b' : 'b,a') + ', 0.0, 1.0 ); ',
+ ' } ',
+ ' '
);
+}
- // provides a custom labeler for axis tick marks
- //
- // value : the tick value to create a label string for
- // axis : the axis associated with the tick value
- // tickInterval : the requested spacing in pixels between ticks
- // precision : number of decimal points which should be used for tick labels
- // orderAxis : order( Math.abs( axis.vSize ) ) then rounded to nearest multiple of three (-3, 0, 3, 6...)
- // orderFactor : Math.pow( 10, -orderAxis )
- export interface TickLabeler {
- ( value : number, axis : Axis1D, tickInterval : number ): string;
- }
-
-
- export interface EdgeAxisPainterOptions {
- tickSpacing? : number;
- label? : string;
- units? : string;
- shortenLabels? : boolean;
- font? : string;
- textColor? : Color;
- tickColor? : Color;
- tickSize? : number;
- showLabel? : boolean;
- showBorder? : boolean;
- gradientFill?: Gradient;
- tickLabeler? : TickLabeler;
- }
+export let gradient_FRAGSHADER = concatLines(
+ ' ',
+ ' precision highp float; ',
+ ' uniform sampler2D u_colorTex; ',
+ ' ',
+ ' varying vec2 v_texCoord; ',
+ ' ',
+ ' void main( ) { ',
+ ' vec4 color = texture2D( u_colorTex, v_texCoord ); ',
+ ' gl_FragColor = color; ',
+ ' gl_FragColor.a = 1.0; ',
+ ' } '
+);
+
+// provides a custom labeler for axis tick marks
+//
+// value : the tick value to create a label string for
+// axis : the axis associated with the tick value
+// tickInterval : the requested spacing in pixels between ticks
+// precision : number of decimal points which should be used for tick labels
+// orderAxis : order( Math.abs( axis.vSize ) ) then rounded to nearest multiple of three (-3, 0, 3, 6...)
+// orderFactor : Math.pow( 10, -orderAxis )
+export type TickLabeler = (value: number, axis: Axis1D, tickInterval: number) => string;
+
+
+export interface EdgeAxisPainterOptions {
+ tickSpacing?: number;
+ label?: string;
+ units?: string;
+ shortenLabels?: boolean;
+ font?: string;
+ textColor?: Color;
+ tickColor?: Color;
+ tickSize?: number;
+ showLabel?: boolean;
+ showBorder?: boolean;
+ gradientFill?: Gradient;
+ tickLabeler?: TickLabeler;
+}
- export function newEdgeAxisPainter( axis : Axis1D, labelSide : Side, options? : EdgeAxisPainterOptions ) : Painter {
- var tickSpacing = ( hasval( options ) && hasval( options.tickSpacing ) ? options.tickSpacing : 100 );
- var label = ( hasval( options ) && hasval( options.label ) ? options.label : '' );
- var units = ( hasval( options ) && hasval( options.units ) ? options.units : '' );
- var shortenLabels = ( hasval( options ) && hasval( options.shortenLabels ) ? options.shortenLabels : true );
- var font = ( hasval( options ) && hasval( options.font ) ? options.font : '11px verdana,sans-serif' );
- var textColor = ( hasval( options ) && hasval( options.textColor ) ? options.textColor : black );
- var tickColor = ( hasval( options ) && hasval( options.tickColor ) ? options.tickColor : black );
- var tickSize = ( hasval( options ) && hasval( options.tickSize ) ? options.tickSize : 6 );
- var showLabel = ( hasval( options ) && hasval( options.showLabel ) ? options.showLabel : true );
- var showBorder = ( hasval( options ) && hasval( options.showBorder ) ? options.showBorder : false );
- var gradientFill = ( hasval( options ) && hasval( options.gradientFill ) ? options.gradientFill : undefined );
- var tickLabeler = ( hasval( options ) && hasval( options.tickLabeler ) ? options.tickLabeler : undefined );
-
- var tickPositions = new Float32Array( 0 );
-
- var gradientProgram = new Program( heatmap_VERTSHADER, gradient_FRAGSHADER );
- var gradientProgram_u_modelViewMatrix = new UniformMatrix4f( gradientProgram, 'u_modelViewMatrix' );
- var gradientProgram_u_colorTexture = new UniformSampler2D( gradientProgram, 'u_colorTex' );
- var gradientProgram_a_vertCoord = new Attribute( gradientProgram, 'a_vertCoord' );
- var gradientProgram_a_texCoord = new Attribute( gradientProgram, 'a_texCoord' );
-
- if ( gradientFill ) var gradientColorTexture = getGradientTexture( gradientFill );
-
- var gradientVertCoords = new Float32Array( 0 );
- var gradientVertCoordsBuffer = newDynamicBuffer( );
-
- var gradientTexCoords = new Float32Array( 0 );
- var gradientTexCoordsBuffer = newDynamicBuffer( );
-
- var borderProgram = new Program( modelview_VERTSHADER, solid_FRAGSHADER );
- var borderProgram_a_Position = new Attribute( borderProgram, 'a_Position' );
- var borderProgram_u_modelViewMatrix = new UniformMatrix4f( borderProgram, 'u_modelViewMatrix' );
- var borderProgram_u_Color = new UniformColor( borderProgram, 'u_Color' );
-
- var borderCoords = new Float32Array( 0 );
- var borderCoordsBuffer = newDynamicBuffer( );
-
- var marksProgram = new Program( edgeMarks_VERTSHADER( labelSide ), solid_FRAGSHADER );
- var marksProgram_u_VMin = new Uniform1f( marksProgram, 'u_VMin' );
- var marksProgram_u_VSize = new Uniform1f( marksProgram, 'u_VSize' );
- var marksProgram_u_ViewportSize = new Uniform2f( marksProgram, 'u_ViewportSize' );
- var marksProgram_u_MarkSize = new Uniform1f( marksProgram, 'u_MarkSize' );
- var marksProgram_u_Color = new UniformColor( marksProgram, 'u_Color' );
- var marksProgram_a_VCoord = new Attribute( marksProgram, 'a_VCoord' );
-
- var markCoords = new Float32Array( 0 );
- var markCoordsBuffer = newDynamicBuffer( );
-
- var textTextures = > newTextTextureCache( font, textColor );
- var textureRenderer = new TextureRenderer( );
- var hTickLabels = textTextures.value( '-0.123456789' ).h;
- var isVerticalAxis = ( labelSide === Side.LEFT || labelSide === Side.RIGHT );
-
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
-
- var sizePixels = isVerticalAxis ? viewport.h : viewport.w;
- if ( sizePixels === 0 ) return;
- var approxNumTicks = sizePixels / tickSpacing;
- var tickInterval = getTickInterval( axis, approxNumTicks );
- var tickCount = getTickCount( axis, tickInterval );
- tickPositions = ensureCapacityFloat32( tickPositions, tickCount );
- getTickPositions( axis, tickInterval, tickCount, tickPositions );
-
-
- // Border Box and Gradient Fill
- //
-
- //XXX border vertices are fixed in normalized 0-1 viewport coordinates
- //XXX they could be calculated ahead of time -- however I had trouble with 'fuzzy' lines when using 0-1 coordinates
- if ( showBorder || gradientFill ) {
- borderCoords = ensureCapacityFloat32( borderCoords, 10 );
-
- var horizontal = ( labelSide === Side.TOP || labelSide === Side.BOTTOM );
- var bFlip = ( labelSide === Side.LEFT || labelSide === Side.BOTTOM );
- var width = viewport.w - 1;
- var height = viewport.h - 1;
-
- borderCoords[0] = horizontal ? 0 : ( bFlip ? width - tickSize : 0 );
- borderCoords[1] = !horizontal ? 0 : ( bFlip ? height - tickSize : 0 );
-
- borderCoords[2] = horizontal ? 0 : ( bFlip ? width : tickSize );
- borderCoords[3] = !horizontal ? 0 : ( bFlip ? height : tickSize );
-
- borderCoords[4] = horizontal ? width : ( bFlip ? width : tickSize );
- borderCoords[5] = !horizontal ? height : ( bFlip ? height : tickSize );
-
- borderCoords[6] = horizontal ? width : ( bFlip ? width - tickSize : 0 );
- borderCoords[7] = !horizontal ? height : ( bFlip ? height - tickSize : 0 );
-
- // finish off the box (same as 0, 1 coordinates)
- borderCoords[8] = horizontal ? 0 : ( bFlip ? width - tickSize : 0 );
- borderCoords[9] = !horizontal ? 0 : ( bFlip ? height - tickSize : 0 );
- }
-
- if ( gradientFill ) {
- gradientProgram.use( gl );
- gradientProgram_u_modelViewMatrix.setData( gl, glOrthoViewport( viewport ) );
- gradientProgram_u_colorTexture.setDataAndBind( gl, 0, gradientColorTexture );
-
- gradientVertCoords = ensureCapacityFloat32( gradientVertCoords, 8 );
- gradientVertCoords[0] = borderCoords[2];
- gradientVertCoords[1] = borderCoords[3];
- gradientVertCoords[2] = borderCoords[0];
- gradientVertCoords[3] = borderCoords[1];
- gradientVertCoords[4] = borderCoords[4];
- gradientVertCoords[5] = borderCoords[5];
- gradientVertCoords[6] = borderCoords[6];
- gradientVertCoords[7] = borderCoords[7];
- gradientVertCoordsBuffer.setData( gradientVertCoords );
- gradientProgram_a_vertCoord.setDataAndEnable( gl, gradientVertCoordsBuffer, 2, GL.FLOAT );
-
- // y texture coordinates don't really matter ( we're simulating a 1d texture )
- // using a 1-by-n 2d texture because 1d textures aren't available
- gradientTexCoords = ensureCapacityFloat32( gradientTexCoords, 8 );
- gradientTexCoords[0] = 0;
- gradientTexCoords[1] = 0;
- gradientTexCoords[2] = 0;
- gradientTexCoords[3] = 0;
- gradientTexCoords[4] = 1;
- gradientTexCoords[5] = 1;
- gradientTexCoords[6] = 1;
- gradientTexCoords[7] = 1;
- gradientTexCoordsBuffer.setData( gradientTexCoords );
- gradientProgram_a_texCoord.setDataAndEnable( gl, gradientTexCoordsBuffer, 2, GL.FLOAT );
-
- gl.drawArrays( GL.TRIANGLE_STRIP, 0, 4 );
-
- gradientProgram_u_colorTexture.unbind( gl );
- gradientProgram_a_vertCoord.disable( gl );
- gradientProgram_a_texCoord.disable( gl );
- gradientProgram.endUse( gl );
- }
-
- if ( showBorder ) {
- borderProgram.use( gl );
- borderProgram_u_Color.setData( gl, tickColor );
- borderProgram_u_modelViewMatrix.setData( gl, glOrthoViewport( viewport ) );
-
- borderCoordsBuffer.setData( borderCoords.subarray( 0, 10 ) );
- borderProgram_a_Position.setDataAndEnable( gl, borderCoordsBuffer, 2, GL.FLOAT );
-
- // IE does not support lineWidths other than 1, so make sure all browsers use lineWidth of 1
- gl.lineWidth( 1 );
- gl.drawArrays( GL.LINE_STRIP, 0, 5 );
-
- borderProgram_a_Position.disable( gl );
- borderProgram.endUse( gl );
- }
+export function newEdgeAxisPainter(axis: Axis1D, labelSide: Side, options?: EdgeAxisPainterOptions): Painter {
+ const tickSpacing = (hasval(options) && hasval(options.tickSpacing) ? options.tickSpacing : 100);
+ const label = (hasval(options) && hasval(options.label) ? options.label : '');
+ const units = (hasval(options) && hasval(options.units) ? options.units : '');
+ const shortenLabels = (hasval(options) && hasval(options.shortenLabels) ? options.shortenLabels : true);
+ const font = (hasval(options) && hasval(options.font) ? options.font : '11px verdana,sans-serif');
+ const textColor = (hasval(options) && hasval(options.textColor) ? options.textColor : black);
+ const tickColor = (hasval(options) && hasval(options.tickColor) ? options.tickColor : black);
+ const tickSize = (hasval(options) && hasval(options.tickSize) ? options.tickSize : 6);
+ const showLabel = (hasval(options) && hasval(options.showLabel) ? options.showLabel : true);
+ const showBorder = (hasval(options) && hasval(options.showBorder) ? options.showBorder : false);
+ const gradientFill = (hasval(options) && hasval(options.gradientFill) ? options.gradientFill : undefined);
+ const tickLabeler = (hasval(options) && hasval(options.tickLabeler) ? options.tickLabeler : undefined);
+
+ let tickPositions = new Float32Array(0);
+
+ const gradientProgram = new Program(heatmap_VERTSHADER, gradient_FRAGSHADER);
+ const gradientProgram_u_modelViewMatrix = new UniformMatrix4f(gradientProgram, 'u_modelViewMatrix');
+ const gradientProgram_u_colorTexture = new UniformSampler2D(gradientProgram, 'u_colorTex');
+ const gradientProgram_a_vertCoord = new Attribute(gradientProgram, 'a_vertCoord');
+ const gradientProgram_a_texCoord = new Attribute(gradientProgram, 'a_texCoord');
+
+ let gradientColorTexture: Texture = null;
+ if (gradientFill) {
+ gradientColorTexture = getGradientTexture(gradientFill);
+ }
- // Tick marks
- //
-
- marksProgram.use( gl );
- marksProgram_u_VMin.setData( gl, axis.vMin );
- marksProgram_u_VSize.setData( gl, axis.vSize );
- marksProgram_u_ViewportSize.setData( gl, viewport.w, viewport.h );
- marksProgram_u_MarkSize.setData( gl, tickSize );
- marksProgram_u_Color.setData( gl, tickColor );
-
- markCoords = ensureCapacityFloat32( markCoords, 4*tickCount );
- for ( var n = 0; n < tickCount; n++ ) {
- var v = tickPositions[ n ];
- markCoords[ ( 4*n + 0 ) ] = v;
- markCoords[ ( 4*n + 1 ) ] = 0;
- markCoords[ ( 4*n + 2 ) ] = v;
- markCoords[ ( 4*n + 3 ) ] = 1;
- }
- markCoordsBuffer.setData( markCoords.subarray( 0, 4*tickCount ) );
- marksProgram_a_VCoord.setDataAndEnable( gl, markCoordsBuffer, 2, GL.FLOAT );
+ let gradientVertCoords = new Float32Array(0);
+ const gradientVertCoordsBuffer = newDynamicBuffer();
+
+ let gradientTexCoords = new Float32Array(0);
+ const gradientTexCoordsBuffer = newDynamicBuffer();
+
+ const borderProgram = new Program(modelview_VERTSHADER, solid_FRAGSHADER);
+ const borderProgram_a_Position = new Attribute(borderProgram, 'a_Position');
+ const borderProgram_u_modelViewMatrix = new UniformMatrix4f(borderProgram, 'u_modelViewMatrix');
+ const borderProgram_u_Color = new UniformColor(borderProgram, 'u_Color');
+
+ let borderCoords = new Float32Array(0);
+ const borderCoordsBuffer = newDynamicBuffer();
+
+ const marksProgram = new Program(edgeMarks_VERTSHADER(labelSide), solid_FRAGSHADER);
+ const marksProgram_u_VMin = new Uniform1f(marksProgram, 'u_VMin');
+ const marksProgram_u_VSize = new Uniform1f(marksProgram, 'u_VSize');
+ const marksProgram_u_ViewportSize = new Uniform2f(marksProgram, 'u_ViewportSize');
+ const marksProgram_u_MarkSize = new Uniform1f(marksProgram, 'u_MarkSize');
+ const marksProgram_u_Color = new UniformColor(marksProgram, 'u_Color');
+ const marksProgram_a_VCoord = new Attribute(marksProgram, 'a_VCoord');
+
+ let markCoords = new Float32Array(0);
+ const markCoordsBuffer = newDynamicBuffer();
+
+ const textTextures = >newTextTextureCache(font, textColor);
+ const textureRenderer = new TextureRenderer();
+ const hTickLabels = textTextures.value('-0.123456789').h;
+ const isVerticalAxis = (labelSide === Side.LEFT || labelSide === Side.RIGHT);
+
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+
+ const sizePixels = isVerticalAxis ? viewport.h : viewport.w;
+ if (sizePixels === 0) {
+ return;
+ }
+ const approxNumTicks = sizePixels / tickSpacing;
+ const tickInterval = getTickInterval(axis, approxNumTicks);
+ const tickCount = getTickCount(axis, tickInterval);
+ tickPositions = ensureCapacityFloat32(tickPositions, tickCount);
+ getTickPositions(axis, tickInterval, tickCount, tickPositions);
+
+
+ // Border Box and Gradient Fill
+ //
+
+ // XXX border vertices are fixed in normalized 0-1 viewport coordinates
+ // XXX they could be calculated ahead of time -- however I had trouble with 'fuzzy' lines when using 0-1 coordinates
+ if (showBorder || gradientFill) {
+ borderCoords = ensureCapacityFloat32(borderCoords, 10);
+
+ const horizontal = (labelSide === Side.TOP || labelSide === Side.BOTTOM);
+ const bFlip = (labelSide === Side.LEFT || labelSide === Side.BOTTOM);
+ const width = viewport.w - 1;
+ const height = viewport.h - 1;
+
+ borderCoords[0] = horizontal ? 0 : (bFlip ? width - tickSize : 0);
+ borderCoords[1] = !horizontal ? 0 : (bFlip ? height - tickSize : 0);
+
+ borderCoords[2] = horizontal ? 0 : (bFlip ? width : tickSize);
+ borderCoords[3] = !horizontal ? 0 : (bFlip ? height : tickSize);
+
+ borderCoords[4] = horizontal ? width : (bFlip ? width : tickSize);
+ borderCoords[5] = !horizontal ? height : (bFlip ? height : tickSize);
+
+ borderCoords[6] = horizontal ? width : (bFlip ? width - tickSize : 0);
+ borderCoords[7] = !horizontal ? height : (bFlip ? height - tickSize : 0);
+
+ // finish off the box (same as 0, 1 coordinates)
+ borderCoords[8] = horizontal ? 0 : (bFlip ? width - tickSize : 0);
+ borderCoords[9] = !horizontal ? 0 : (bFlip ? height - tickSize : 0);
+ }
+
+ if (gradientFill) {
+ gradientProgram.use(gl);
+ gradientProgram_u_modelViewMatrix.setData(gl, glOrthoViewport(viewport));
+ gradientProgram_u_colorTexture.setDataAndBind(gl, 0, gradientColorTexture);
+
+ gradientVertCoords = ensureCapacityFloat32(gradientVertCoords, 8);
+ gradientVertCoords[0] = borderCoords[2];
+ gradientVertCoords[1] = borderCoords[3];
+ gradientVertCoords[2] = borderCoords[0];
+ gradientVertCoords[3] = borderCoords[1];
+ gradientVertCoords[4] = borderCoords[4];
+ gradientVertCoords[5] = borderCoords[5];
+ gradientVertCoords[6] = borderCoords[6];
+ gradientVertCoords[7] = borderCoords[7];
+ gradientVertCoordsBuffer.setData(gradientVertCoords);
+ gradientProgram_a_vertCoord.setDataAndEnable(gl, gradientVertCoordsBuffer, 2, GL.FLOAT);
+
+ // y texture coordinates don't really matter ( we're simulating a 1d texture )
+ // using a 1-by-n 2d texture because 1d textures aren't available
+ gradientTexCoords = ensureCapacityFloat32(gradientTexCoords, 8);
+ gradientTexCoords[0] = 0;
+ gradientTexCoords[1] = 0;
+ gradientTexCoords[2] = 0;
+ gradientTexCoords[3] = 0;
+ gradientTexCoords[4] = 1;
+ gradientTexCoords[5] = 1;
+ gradientTexCoords[6] = 1;
+ gradientTexCoords[7] = 1;
+ gradientTexCoordsBuffer.setData(gradientTexCoords);
+ gradientProgram_a_texCoord.setDataAndEnable(gl, gradientTexCoordsBuffer, 2, GL.FLOAT);
+
+ gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
+
+ gradientProgram_u_colorTexture.unbind(gl);
+ gradientProgram_a_vertCoord.disable(gl);
+ gradientProgram_a_texCoord.disable(gl);
+ gradientProgram.endUse(gl);
+ }
+
+ if (showBorder) {
+ borderProgram.use(gl);
+ borderProgram_u_Color.setData(gl, tickColor);
+ borderProgram_u_modelViewMatrix.setData(gl, glOrthoViewport(viewport));
+
+ borderCoordsBuffer.setData(borderCoords.subarray(0, 10));
+ borderProgram_a_Position.setDataAndEnable(gl, borderCoordsBuffer, 2, GL.FLOAT);
// IE does not support lineWidths other than 1, so make sure all browsers use lineWidth of 1
- gl.lineWidth( 1 );
- gl.drawArrays( GL.LINES, 0, 2*tickCount );
-
- marksProgram_a_VCoord.disable( gl );
- marksProgram.endUse( gl );
-
-
- // Tick labels
- //
-
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
+ gl.lineWidth(1);
+ gl.drawArrays(GL.LINE_STRIP, 0, 5);
+
+ borderProgram_a_Position.disable(gl);
+ borderProgram.endUse(gl);
+ }
+
+ // Tick marks
+ //
+
+ marksProgram.use(gl);
+ marksProgram_u_VMin.setData(gl, axis.vMin);
+ marksProgram_u_VSize.setData(gl, axis.vSize);
+ marksProgram_u_ViewportSize.setData(gl, viewport.w, viewport.h);
+ marksProgram_u_MarkSize.setData(gl, tickSize);
+ marksProgram_u_Color.setData(gl, tickColor);
+
+ markCoords = ensureCapacityFloat32(markCoords, 4 * tickCount);
+ for (let n = 0; n < tickCount; n++) {
+ const v = tickPositions[n];
+ markCoords[(4 * n + 0)] = v;
+ markCoords[(4 * n + 1)] = 0;
+ markCoords[(4 * n + 2)] = v;
+ markCoords[(4 * n + 3)] = 1;
+ }
+ markCoordsBuffer.setData(markCoords.subarray(0, 4 * tickCount));
+ marksProgram_a_VCoord.setDataAndEnable(gl, markCoordsBuffer, 2, GL.FLOAT);
+
+ // IE does not support lineWidths other than 1, so make sure all browsers use lineWidth of 1
+ gl.lineWidth(1);
+ gl.drawArrays(GL.LINES, 0, 2 * tickCount);
+
+ marksProgram_a_VCoord.disable(gl);
+ marksProgram.endUse(gl);
+
+
+ // Tick labels
+ //
+
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ const orderAxisRaw = order(Math.abs(axis.vSize));
+ let orderAxis = 0;
+ if (orderAxisRaw > 0) {
+ orderAxis = Math.floor((orderAxisRaw - 1) / 3) * 3;
+ }
+ else if (orderAxisRaw < 0) {
+ orderAxis = (Math.ceil(orderAxisRaw / 3) - 1) * 3;
+ }
+ const orderFactor = Math.pow(10, -orderAxis);
+ const orderTick = order(tickInterval);
+ const precision = Math.max(0, orderAxis - orderTick);
+
+ textTextures.resetTouches();
+ textureRenderer.begin(gl, viewport);
+
+ for (let n = 0; n < tickCount; n++) {
+ const v = tickPositions[n];
+ const vFrac = axis.vFrac(v);
+ if (vFrac < 0 || vFrac >= 1) {
+ continue;
+ }
- var orderAxisRaw = order( Math.abs( axis.vSize ) );
- var orderAxis = 0;
- if ( orderAxisRaw > 0 ) {
- orderAxis = Math.floor( ( orderAxisRaw - 1 ) / 3 ) * 3;
+ let tickLabel;
+ if (tickLabeler) {
+ // show custom tick value
+ tickLabel = tickLabeler(v, axis, tickInterval);
}
- else if ( orderAxisRaw < 0 ) {
- orderAxis = ( Math.ceil( orderAxisRaw / 3 ) - 1 ) * 3;
+ else if (shortenLabels && showLabel) {
+ // show shortened tick value
+ tickLabel = Number(v * orderFactor).toFixed(precision);
}
- var orderFactor = Math.pow( 10, -orderAxis );
- var orderTick = order( tickInterval );
- var precision = Math.max( 0, orderAxis - orderTick );
-
- textTextures.resetTouches( );
- textureRenderer.begin( gl, viewport );
-
- for ( var n = 0; n < tickCount; n++ ) {
- var v = tickPositions[ n ];
- var vFrac = axis.vFrac( v );
- if ( vFrac < 0 || vFrac >= 1 ) continue;
-
- var tickLabel;
- if ( tickLabeler ) {
- // show custom tick value
- tickLabel = tickLabeler( v, axis, tickInterval );
- }
- else if ( shortenLabels && showLabel ) {
- // show shortened tick value
- tickLabel = Number( v * orderFactor ).toFixed( precision );
- }
- else if ( !shortenLabels ) {
- // show actual tick value
- if ( orderAxisRaw >= 0 ) {
- tickLabel = Number( v ).toFixed( 0 );
- }
- else {
- tickLabel = Number( v ).toFixed( -orderAxisRaw );
- }
+ else if (!shortenLabels) {
+ // show actual tick value
+ if (orderAxisRaw >= 0) {
+ tickLabel = Number(v).toFixed(0);
}
else {
- // show magnitude inline for each tick
- tickLabel = Number( v * orderFactor ).toFixed( precision ) + ( orderAxis === 0 ? '' : 'e' + orderAxis );
+ tickLabel = Number(v).toFixed(-orderAxisRaw);
}
- var textTexture = textTextures.value( tickLabel );
-
- var xFrac : number;
- var yFrac : number;
- if ( labelSide === Side.LEFT || labelSide === Side.RIGHT ) {
- var yAnchor = textTexture.yAnchor( 0.43 );
- var j0 = ( vFrac * viewport.h ) - yAnchor*textTexture.h;
- var j = clamp( 0, viewport.h - textTexture.h, j0 );
- yFrac = j / viewport.h;
-
- if ( labelSide === Side.LEFT ) {
- xFrac = ( viewport.w - tickSize - 2 - textTexture.w ) / viewport.w;
- }
- else {
- xFrac = ( tickSize + 2 ) / viewport.w;
- }
+ }
+ else {
+ // show magnitude inline for each tick
+ tickLabel = Number(v * orderFactor).toFixed(precision) + (orderAxis === 0 ? '' : 'e' + orderAxis);
+ }
+ const textTexture = textTextures.value(tickLabel);
+
+ let xFrac: number;
+ let yFrac: number;
+ if (labelSide === Side.LEFT || labelSide === Side.RIGHT) {
+ const yAnchor = textTexture.yAnchor(0.43);
+ const j0 = (vFrac * viewport.h) - yAnchor * textTexture.h;
+ const j = clamp(0, viewport.h - textTexture.h, j0);
+ yFrac = j / viewport.h;
+
+ if (labelSide === Side.LEFT) {
+ xFrac = (viewport.w - tickSize - 2 - textTexture.w) / viewport.w;
}
else {
- var wMinus = 0;
- if ( v < 0 ) {
- var absTickLabel = Number( Math.abs( v ) * orderFactor ).toFixed( precision );
- wMinus = textTexture.w - textTextures.value( absTickLabel ).w;
- }
-
- var xAnchor = 0.45;
- var i0 = ( vFrac * viewport.w ) - xAnchor*( textTexture.w - wMinus ) - wMinus;
- var i = clamp( 0, viewport.w - textTexture.w, i0 );
- xFrac = i / viewport.w;
-
- if ( labelSide === Side.BOTTOM ) {
- yFrac = ( viewport.h - tickSize - 2 - hTickLabels ) / viewport.h;
- }
- else {
- yFrac = ( tickSize + 2 ) / viewport.h;
- }
+ xFrac = (tickSize + 2) / viewport.w;
}
- textureRenderer.draw( gl, textTexture, xFrac, yFrac, { xAnchor: 0, yAnchor: 0 } );
}
+ else {
+ let wMinus = 0;
+ if (v < 0) {
+ const absTickLabel = Number(Math.abs(v) * orderFactor).toFixed(precision);
+ wMinus = textTexture.w - textTextures.value(absTickLabel).w;
+ }
+ const xAnchor = 0.45;
+ const i0 = (vFrac * viewport.w) - xAnchor * (textTexture.w - wMinus) - wMinus;
+ const i = clamp(0, viewport.w - textTexture.w, i0);
+ xFrac = i / viewport.w;
- // Axis label
- //
-
- if ( showLabel ) {
- var unitsString = units + ( !shortenLabels || orderAxis === 0 ? '' : ' x 10^' + orderAxis.toFixed( 0 ) );
- var axisLabel = label + ( unitsString ? ' (' + unitsString + ')' : '' );
-
- if ( axisLabel !== '' ) {
- var textTexture = textTextures.value( axisLabel );
-
- var xFrac : number;
- var yFrac : number;
- var textOpts : TextureDrawOptions;
- if ( labelSide === Side.LEFT || labelSide === Side.RIGHT ) {
- // Using hTickLabels here works out about right, even though the tick-label text is horizontal
- var xFrac0 = 0.5 * ( viewport.w - tickSize - 2 - hTickLabels ) / viewport.w;
- xFrac = ( labelSide === Side.LEFT ? xFrac0 : 1 - xFrac0 );
- yFrac = 0.5;
- textOpts = { xAnchor: textTexture.yAnchor( 0.5 ),
- yAnchor: 0.5,
- rotation_CCWRAD: 0.5 * Math.PI };
- }
- else {
- var yFrac0 = 0.5 * ( viewport.h - tickSize - 2 - hTickLabels ) / viewport.h;
- yFrac = ( labelSide === Side.BOTTOM ? yFrac0 : 1 - yFrac0 );
- xFrac = 0.5;
- textOpts = { xAnchor: 0.5,
- yAnchor: textTexture.yAnchor( 0.5 ),
- rotation_CCWRAD: 0 };
- }
- textureRenderer.draw( gl, textTexture, xFrac, yFrac, textOpts );
+ if (labelSide === Side.BOTTOM) {
+ yFrac = (viewport.h - tickSize - 2 - hTickLabels) / viewport.h;
+ }
+ else {
+ yFrac = (tickSize + 2) / viewport.h;
}
}
+ textureRenderer.draw(gl, textTexture, xFrac, yFrac, { xAnchor: 0, yAnchor: 0 });
+ }
+
+
+ // Axis label
+ //
+
+ if (showLabel) {
+ const unitsString = units + (!shortenLabels || orderAxis === 0 ? '' : ' x 10^' + orderAxis.toFixed(0));
+ const axisLabel = label + (unitsString ? ' (' + unitsString + ')' : '');
+
+ if (axisLabel !== '') {
+ const textTexture = textTextures.value(axisLabel);
+
+ let xFrac: number;
+ let yFrac: number;
+ let textOpts: TextureDrawOptions;
+ if (labelSide === Side.LEFT || labelSide === Side.RIGHT) {
+ // Using hTickLabels here works out about right, even though the tick-label text is horizontal
+ const xFrac0 = 0.5 * (viewport.w - tickSize - 2 - hTickLabels) / viewport.w;
+ xFrac = (labelSide === Side.LEFT ? xFrac0 : 1 - xFrac0);
+ yFrac = 0.5;
+ textOpts = {
+ xAnchor: textTexture.yAnchor(0.5),
+ yAnchor: 0.5,
+ rotation_CCWRAD: 0.5 * Math.PI
+ };
+ }
+ else {
+ const yFrac0 = 0.5 * (viewport.h - tickSize - 2 - hTickLabels) / viewport.h;
+ yFrac = (labelSide === Side.BOTTOM ? yFrac0 : 1 - yFrac0);
+ xFrac = 0.5;
+ textOpts = {
+ xAnchor: 0.5,
+ yAnchor: textTexture.yAnchor(0.5),
+ rotation_CCWRAD: 0
+ };
+ }
+ textureRenderer.draw(gl, textTexture, xFrac, yFrac, textOpts);
+ }
+ }
- // Finish up
- //
-
- textureRenderer.end( gl );
- textTextures.retainTouched( );
- };
- }
+ // Finish up
+ //
+ textureRenderer.end(gl);
+ textTextures.retainTouched();
+ };
}
+
+
diff --git a/src/webglimpse/plot/heatmap_painter.ts b/src/webglimpse/plot/heatmap_painter.ts
index cad5df5..20b3924 100644
--- a/src/webglimpse/plot/heatmap_painter.ts
+++ b/src/webglimpse/plot/heatmap_painter.ts
@@ -27,138 +27,145 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
- export interface HeatmapPainterOptions {
- blend? : boolean;
- }
-
- export interface HeatMapData {
- // matrix data stored x-major
- array : Float32Array;
-
- // size of the matrix
- xSize : number;
- ySize : number;
-
- // bounds of the matrix in axis coordinates
- xMin : number;
- xMax : number;
- yMin : number;
- yMax : number;
- }
-
-
- export var heatmap_VERTSHADER = concatLines(
- ' ',
- ' uniform mat4 u_modelViewMatrix; ',
- ' attribute vec4 a_vertCoord; ',
- ' attribute vec2 a_texCoord; ',
- ' varying vec2 v_texCoord; ',
- ' ',
- ' void main( ) { ',
- ' gl_Position = u_modelViewMatrix * a_vertCoord; ',
- ' v_texCoord = a_texCoord; ',
- ' } ',
- ' '
- );
-
-
- export var heatmap_FRAGSHADER = concatLines(
- ' ',
- ' precision highp float; ',
- ' uniform sampler2D u_dataTex; ',
- ' uniform sampler2D u_colorTex; ',
- ' uniform float u_dataMin; ',
- ' uniform float u_dataMax; ',
- ' ',
- ' varying vec2 v_texCoord; ',
- ' ',
- ' void main() ',
- ' { ',
- ' float dataVal = texture2D( u_dataTex, v_texCoord ).r; ',
- ' float normalizedVal = ( dataVal - u_dataMin ) / ( u_dataMax - u_dataMin ); ',
- ' clamp( normalizedVal, 0.0, 1.0 ); ',
- ' ',
- ' vec4 color = texture2D( u_colorTex, vec2( normalizedVal, 0 ) ); ',
- ' gl_FragColor = color; ',
- ' gl_FragColor.a = 1.0; ',
- ' } '
- );
-
- /**
- * Simple heatmap painter which displays a 2d matrix of static data
- */
- export function newHeatmapPainter( axis : Axis2D, colorAxis : Axis1D, data : HeatMapData, colorTexture : Texture, options? : HeatmapPainterOptions ) : Painter {
-
- var blend = ( hasval( options ) && hasval( options.blend ) ? options.blend : false );
-
- // only GL_RGBA is supported with GL_FLOAT texture type in webgl (see texture.ts)
- // we we currently need an array 4 times bigger than necessary in order to use FLOATS
- // to store the matrix data in a texture
- var array = new Float32Array( data.xSize * data.ySize * 4 );
- for ( var x = 0 ; x < data.xSize ; x++ ) {
- for ( var y = 0 ; y < data.ySize ; y++ ) {
- var index = x * data.ySize + y;
- var value = data.array[ index ];
-
- array[ 4*index ] = value;
- array[ 4*index + 1 ] = value;
- array[ 4*index + 2 ] = value;
- array[ 4*index + 3 ] = value;
- }
+import { concatLines, GL, hasval } from '../util/util';
+import { Axis2D, Axis1D } from './axis';
+import { Texture, FloatDataTexture2D } from '../texture';
+import { Painter } from '../core';
+import { Program, UniformMatrix4f, UniformSampler2D, Uniform1f, Attribute } from '../shader';
+import { newStaticBuffer } from '../buffer';
+import { BoundsUnmodifiable } from '../bounds';
+import { glOrthoAxis } from '../matrix';
+
+
+export interface HeatmapPainterOptions {
+ blend?: boolean;
+}
+
+export interface HeatMapData {
+ // matrix data stored x-major
+ array: Float32Array;
+
+ // size of the matrix
+ xSize: number;
+ ySize: number;
+
+ // bounds of the matrix in axis coordinates
+ xMin: number;
+ xMax: number;
+ yMin: number;
+ yMax: number;
+}
+
+
+export let heatmap_VERTSHADER = concatLines(
+ ' ',
+ ' uniform mat4 u_modelViewMatrix; ',
+ ' attribute vec4 a_vertCoord; ',
+ ' attribute vec2 a_texCoord; ',
+ ' varying vec2 v_texCoord; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_Position = u_modelViewMatrix * a_vertCoord; ',
+ ' v_texCoord = a_texCoord; ',
+ ' } ',
+ ' '
+);
+
+
+export let heatmap_FRAGSHADER = concatLines(
+ ' ',
+ ' precision highp float; ',
+ ' uniform sampler2D u_dataTex; ',
+ ' uniform sampler2D u_colorTex; ',
+ ' uniform float u_dataMin; ',
+ ' uniform float u_dataMax; ',
+ ' ',
+ ' varying vec2 v_texCoord; ',
+ ' ',
+ ' void main() ',
+ ' { ',
+ ' float dataVal = texture2D( u_dataTex, v_texCoord ).r; ',
+ ' float normalizedVal = ( dataVal - u_dataMin ) / ( u_dataMax - u_dataMin ); ',
+ ' clamp( normalizedVal, 0.0, 1.0 ); ',
+ ' ',
+ ' vec4 color = texture2D( u_colorTex, vec2( normalizedVal, 0 ) ); ',
+ ' gl_FragColor = color; ',
+ ' gl_FragColor.a = 1.0; ',
+ ' } '
+);
+
+/**
+ * Simple heatmap painter which displays a 2d matrix of static data
+ */
+export function newHeatmapPainter(axis: Axis2D, colorAxis: Axis1D, data: HeatMapData, colorTexture: Texture, options?: HeatmapPainterOptions): Painter {
+
+ const blend = (hasval(options) && hasval(options.blend) ? options.blend : false);
+
+ // only GL_RGBA is supported with GL_FLOAT texture type in webgl (see texture.ts)
+ // we we currently need an array 4 times bigger than necessary in order to use FLOATS
+ // to store the matrix data in a texture
+ const array = new Float32Array(data.xSize * data.ySize * 4);
+ for (let x = 0; x < data.xSize; x++) {
+ for (let y = 0; y < data.ySize; y++) {
+ const index = x * data.ySize + y;
+ const value = data.array[index];
+
+ array[4 * index] = value;
+ array[4 * index + 1] = value;
+ array[4 * index + 2] = value;
+ array[4 * index + 3] = value;
}
- data.array = array;
-
- var program = new Program( heatmap_VERTSHADER, heatmap_FRAGSHADER );
- var u_modelViewMatrix = new UniformMatrix4f( program, 'u_modelViewMatrix' );
- var u_dataTexture = new UniformSampler2D( program, 'u_dataTex' );
- var u_colorTexture = new UniformSampler2D( program, 'u_colorTex' );
- var u_dataMin = new Uniform1f( program, 'u_dataMin' );
- var u_dataMax = new Uniform1f( program, 'u_dataMax' );
- var a_vertCoord = new Attribute( program, 'a_vertCoord' );
- var a_texCoord = new Attribute( program, 'a_texCoord' );
-
- var texture = new FloatDataTexture2D( data.xSize, data.ySize, data.array );
-
- // points in triangle strip
- var vertCoordArray = [ data.xMin, data.yMax, data.xMax, data.yMax, data.xMin, data.yMin, data.xMax, data.yMin ];
- var vertCoordFloatArray = new Float32Array( vertCoordArray );
- var vertCoordBuffer = newStaticBuffer( vertCoordFloatArray );
-
- // texture coordinates
- var texCoordArray = [ 0, 1, 1, 1, 0, 0, 1, 0 ];
- var texCoordFloatArray = new Float32Array( texCoordArray );
- var texCoordBuffer = newStaticBuffer( texCoordFloatArray );
-
- var dim = 2;
- var vertexCount = 4;
-
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
-
- if ( blend ) {
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
- }
-
- program.use( gl );
- u_dataTexture.setDataAndBind( gl, 0, texture );
- u_colorTexture.setDataAndBind( gl, 1, colorTexture );
- u_modelViewMatrix.setData( gl, glOrthoAxis( axis ) );
-
- u_dataMin.setData( gl, colorAxis.vMin );
- u_dataMax.setData( gl, colorAxis.vMax );
-
- a_vertCoord.setDataAndEnable( gl, vertCoordBuffer, dim, GL.FLOAT );
- a_texCoord.setDataAndEnable( gl, texCoordBuffer, dim, GL.FLOAT );
-
- gl.drawArrays( GL.TRIANGLE_STRIP, 0, vertexCount );
-
- a_vertCoord.disable( gl );
- a_texCoord.disable( gl );
- u_dataTexture.unbind( gl );
- u_colorTexture.unbind( gl );
- program.endUse( gl );
- };
}
- }
\ No newline at end of file
+ data.array = array;
+
+ const program = new Program(heatmap_VERTSHADER, heatmap_FRAGSHADER);
+ const u_modelViewMatrix = new UniformMatrix4f(program, 'u_modelViewMatrix');
+ const u_dataTexture = new UniformSampler2D(program, 'u_dataTex');
+ const u_colorTexture = new UniformSampler2D(program, 'u_colorTex');
+ const u_dataMin = new Uniform1f(program, 'u_dataMin');
+ const u_dataMax = new Uniform1f(program, 'u_dataMax');
+ const a_vertCoord = new Attribute(program, 'a_vertCoord');
+ const a_texCoord = new Attribute(program, 'a_texCoord');
+
+ const texture = new FloatDataTexture2D(data.xSize, data.ySize, data.array);
+
+ // points in triangle strip
+ const vertCoordArray = [data.xMin, data.yMax, data.xMax, data.yMax, data.xMin, data.yMin, data.xMax, data.yMin];
+ const vertCoordFloatArray = new Float32Array(vertCoordArray);
+ const vertCoordBuffer = newStaticBuffer(vertCoordFloatArray);
+
+ // texture coordinates
+ const texCoordArray = [0, 1, 1, 1, 0, 0, 1, 0];
+ const texCoordFloatArray = new Float32Array(texCoordArray);
+ const texCoordBuffer = newStaticBuffer(texCoordFloatArray);
+
+ const dim = 2;
+ const vertexCount = 4;
+
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+
+ if (blend) {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+ }
+
+ program.use(gl);
+ u_dataTexture.setDataAndBind(gl, 0, texture);
+ u_colorTexture.setDataAndBind(gl, 1, colorTexture);
+ u_modelViewMatrix.setData(gl, glOrthoAxis(axis));
+
+ u_dataMin.setData(gl, colorAxis.vMin);
+ u_dataMax.setData(gl, colorAxis.vMax);
+
+ a_vertCoord.setDataAndEnable(gl, vertCoordBuffer, dim, GL.FLOAT);
+ a_texCoord.setDataAndEnable(gl, texCoordBuffer, dim, GL.FLOAT);
+
+ gl.drawArrays(GL.TRIANGLE_STRIP, 0, vertexCount);
+
+ a_vertCoord.disable(gl);
+ a_texCoord.disable(gl);
+ u_dataTexture.unbind(gl);
+ u_colorTexture.unbind(gl);
+ program.endUse(gl);
+ };
+}
diff --git a/src/webglimpse/plot/line_painter.ts b/src/webglimpse/plot/line_painter.ts
index 2421c62..95b2ec7 100644
--- a/src/webglimpse/plot/line_painter.ts
+++ b/src/webglimpse/plot/line_painter.ts
@@ -27,74 +27,81 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Color, black } from '../color';
+import { Axis2D } from './axis';
+import { Painter } from '../core';
+import { Program, UniformColor, UniformMatrix4f } from '../shader';
+import { modelview_VERTSHADER, solid_FRAGSHADER } from '../misc';
+import { newStaticBuffer } from '../buffer';
+import { BoundsUnmodifiable } from '../bounds';
+import { GL, hasval } from '../util/util';
+import { glOrthoAxis } from '../matrix';
+
+export interface XyLinePainterOptions {
+ color?: Color;
+ blend?: boolean;
+ thickness?: number;
+}
- export interface XyLinePainterOptions {
- color? : Color;
- blend? : boolean;
- thickness? : number;
+/**
+ * Simple xy line painter which displays static data
+ */
+export function newXyLinePainter(axis: Axis2D, xCoords: number[], yCoords: number[], options?: XyLinePainterOptions): Painter {
+ const thickness = (hasval(options) && hasval(options.thickness) ? options.thickness : 4);
+ const color = (hasval(options) && hasval(options.color) ? options.color : black);
+ const blend = (hasval(options) && hasval(options.blend) ? options.blend : false);
+
+ const program = new Program(modelview_VERTSHADER, solid_FRAGSHADER);
+ const u_Color = new UniformColor(program, 'u_Color');
+ const u_modelViewMatrix = new UniformMatrix4f(program, 'u_modelViewMatrix');
+
+ const coordArray = [];
+ for (let i = 0; i < xCoords.length; i++) {
+ coordArray[2 * i] = xCoords[i];
+ coordArray[2 * i + 1] = yCoords[i];
}
+ const coordFloatArray = new Float32Array(coordArray);
+ const coordBuffer = newStaticBuffer(coordFloatArray);
+ const dim = 2;
+ const count = coordFloatArray.length / dim;
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
- /**
- * Simple xy line painter which displays static data
- */
- export function newXyLinePainter( axis : Axis2D, xCoords : number[], yCoords : number[], options? : XyLinePainterOptions ) : Painter {
- var thickness = ( hasval( options ) && hasval( options.thickness ) ? options.thickness : 4 );
- var color = ( hasval( options ) && hasval( options.color ) ? options.color : black );
- var blend = ( hasval( options ) && hasval( options.blend ) ? options.blend : false );
-
- var program = new Program( modelview_VERTSHADER, solid_FRAGSHADER );
- var u_Color = new UniformColor( program, 'u_Color' );
- var u_modelViewMatrix = new UniformMatrix4f( program, 'u_modelViewMatrix' );
-
- var coordArray = [];
- for ( var i = 0 ; i < xCoords.length ; i++ ) {
- coordArray[2*i] = xCoords[i];
- coordArray[2*i+1] = yCoords[i];
+ if (blend) {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
}
- var coordFloatArray = new Float32Array( coordArray );
- var coordBuffer = newStaticBuffer( coordFloatArray );
- var dim = 2;
- var count = coordFloatArray.length / dim;
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
+ // enable the shader
+ program.use(gl);
- if ( blend ) {
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
- }
+ // set color and projection matrix variables
+ u_Color.setData(gl, color);
- // enable the shader
- program.use( gl );
+ // set the projection matrix based on the axis bounds
+ u_modelViewMatrix.setData(gl, glOrthoAxis(axis));
- // set color and projection matrix variables
- u_Color.setData( gl, color );
-
- // set the projection matrix based on the axis bounds
- u_modelViewMatrix.setData( gl, glOrthoAxis( axis ) );
+ // XXX: IE doesn't support lineWidth
+ gl.lineWidth(thickness);
- // XXX: IE doesn't support lineWidth
- gl.lineWidth( thickness );
+ // enable vertex attribute array corresponding to vPosition variable in shader
+ gl.enableVertexAttribArray(0);
- // enable vertex attribute array corresponding to vPosition variable in shader
- gl.enableVertexAttribArray( 0 );
+ // bind buffer data to vertex attribute array
+ coordBuffer.bind(gl, GL.ARRAY_BUFFER);
- // bind buffer data to vertex attribute array
- coordBuffer.bind( gl, GL.ARRAY_BUFFER );
-
- // first argument corresponds to the 0 attrib array set above
- // second argument indicates two coordinates per vertex
- gl.vertexAttribPointer( 0, dim, GL.FLOAT, false, 0, 0 );
+ // first argument corresponds to the 0 attrib array set above
+ // second argument indicates two coordinates per vertex
+ gl.vertexAttribPointer(0, dim, GL.FLOAT, false, 0, 0);
- // draw the lines
- gl.drawArrays( GL.LINE_STRIP, 0, count );
+ // draw the lines
+ gl.drawArrays(GL.LINE_STRIP, 0, count);
+
+ coordBuffer.unbind(gl, GL.ARRAY_BUFFER);
+ program.endUse(gl);
+ };
+}
- coordBuffer.unbind( gl, GL.ARRAY_BUFFER );
- program.endUse( gl );
- }
- }
-}
diff --git a/src/webglimpse/plot/plot_layout.ts b/src/webglimpse/plot/plot_layout.ts
index e643085..b06749e 100644
--- a/src/webglimpse/plot/plot_layout.ts
+++ b/src/webglimpse/plot/plot_layout.ts
@@ -27,78 +27,81 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Layout, LayoutEntry } from '../core';
+import { BoundsUnmodifiable } from '../bounds';
+import { Side } from '../misc';
+import { hasval } from '../util/util';
- export function newPlotLayout( options? : { horizAxisHeight? : number; vertAxisWidth? : number } ) : Layout {
- var horizAxisHeight = ( hasval( options ) && hasval( options.horizAxisHeight ) ? options.horizAxisHeight : 60 );
- var vertAxisWidth = ( hasval( options ) && hasval( options.vertAxisWidth ) ? options.vertAxisWidth : 70 );
+export function newPlotLayout(options?: { horizAxisHeight?: number; vertAxisWidth?: number }): Layout {
+ const horizAxisHeight = (hasval(options) && hasval(options.horizAxisHeight) ? options.horizAxisHeight : 60);
+ const vertAxisWidth = (hasval(options) && hasval(options.vertAxisWidth) ? options.vertAxisWidth : 70);
- return {
+ return {
- updateChildViewports: function( children : LayoutEntry[], parentViewport : BoundsUnmodifiable ) {
- var topAxes = [ ];
- var leftAxes = [ ];
- var rightAxes = [ ];
- var bottomAxes = [ ];
- var centers = [ ];
- var others = [ ];
- for ( var c = 0; c < children.length; c++ ) {
- var child = children[ c ];
- switch ( child.layoutArg ) {
- case Side.TOP: topAxes.push( child ); break;
- case Side.LEFT: leftAxes.push( child ); break;
- case Side.RIGHT: rightAxes.push( child ); break;
- case Side.BOTTOM: bottomAxes.push( child ); break;
- case null: centers.push( child ); break;
- default: others.push( child ); break;
- }
+ updateChildViewports: function (children: LayoutEntry[], parentViewport: BoundsUnmodifiable) {
+ const topAxes = [];
+ const leftAxes = [];
+ const rightAxes = [];
+ const bottomAxes = [];
+ const centers = [];
+ const others = [];
+ for (let c = 0; c < children.length; c++) {
+ const child = children[c];
+ switch (child.layoutArg) {
+ case Side.TOP: topAxes.push(child); break;
+ case Side.LEFT: leftAxes.push(child); break;
+ case Side.RIGHT: rightAxes.push(child); break;
+ case Side.BOTTOM: bottomAxes.push(child); break;
+ case null: centers.push(child); break;
+ default: others.push(child); break;
}
+ }
- var numVertAxes = leftAxes.length + rightAxes.length;
- var numHorizAxes = topAxes.length + bottomAxes.length;
- var centerWidth = Math.max( vertAxisWidth, parentViewport.w - numVertAxes*vertAxisWidth );
- var centerHeight = Math.max( horizAxisHeight, parentViewport.h - numHorizAxes*horizAxisHeight );
- var vertAxisWidth2 = ( numVertAxes === 0 ? 0 : ( parentViewport.w - centerWidth )/numVertAxes );
- var horizAxisHeight2 = ( numHorizAxes === 0 ? 0 : ( parentViewport.h - centerHeight )/numHorizAxes );
+ const numVertAxes = leftAxes.length + rightAxes.length;
+ const numHorizAxes = topAxes.length + bottomAxes.length;
+ const centerWidth = Math.max(vertAxisWidth, parentViewport.w - numVertAxes * vertAxisWidth);
+ const centerHeight = Math.max(horizAxisHeight, parentViewport.h - numHorizAxes * horizAxisHeight);
+ const vertAxisWidth2 = (numVertAxes === 0 ? 0 : (parentViewport.w - centerWidth) / numVertAxes);
+ const horizAxisHeight2 = (numHorizAxes === 0 ? 0 : (parentViewport.h - centerHeight) / numHorizAxes);
- var iCenterStart = parentViewport.iStart + leftAxes.length*vertAxisWidth2;
- var iCenterEnd = parentViewport.iEnd - rightAxes.length*vertAxisWidth2;
- var jCenterStart = parentViewport.jStart + bottomAxes.length*horizAxisHeight2;
- var jCenterEnd = parentViewport.jEnd - topAxes.length*horizAxisHeight2;
+ const iCenterStart = parentViewport.iStart + leftAxes.length * vertAxisWidth2;
+ const iCenterEnd = parentViewport.iEnd - rightAxes.length * vertAxisWidth2;
+ const jCenterStart = parentViewport.jStart + bottomAxes.length * horizAxisHeight2;
+ const jCenterEnd = parentViewport.jEnd - topAxes.length * horizAxisHeight2;
- for ( var c = 0; c < topAxes.length; c++ ) {
- var jStart = Math.round( jCenterEnd + c*horizAxisHeight2 );
- var jEnd = ( c === topAxes.length-1 ? parentViewport.jEnd : Math.round( jCenterEnd + (c+1)*horizAxisHeight2 ) );
- topAxes[ c ].viewport.setEdges( iCenterStart, iCenterEnd, jStart, jEnd );
- }
+ for (let c = 0; c < topAxes.length; c++) {
+ const jStart = Math.round(jCenterEnd + c * horizAxisHeight2);
+ const jEnd = (c === topAxes.length - 1 ? parentViewport.jEnd : Math.round(jCenterEnd + (c + 1) * horizAxisHeight2));
+ topAxes[c].viewport.setEdges(iCenterStart, iCenterEnd, jStart, jEnd);
+ }
- for ( var c = 0; c < bottomAxes.length; c++ ) {
- var jStart = ( c === bottomAxes.length-1 ? parentViewport.jStart : Math.round( jCenterStart - (c+1)*horizAxisHeight2 ) );
- var jEnd = Math.round( jCenterStart - c*horizAxisHeight2 );
- bottomAxes[ c ].viewport.setEdges( iCenterStart, iCenterEnd, jStart, jEnd );
- }
+ for (let c = 0; c < bottomAxes.length; c++) {
+ const jStart = (c === bottomAxes.length - 1 ? parentViewport.jStart : Math.round(jCenterStart - (c + 1) * horizAxisHeight2));
+ const jEnd = Math.round(jCenterStart - c * horizAxisHeight2);
+ bottomAxes[c].viewport.setEdges(iCenterStart, iCenterEnd, jStart, jEnd);
+ }
- for ( var c = 0; c < leftAxes.length; c++ ) {
- var iStart = ( c === leftAxes.length-1 ? parentViewport.iStart : Math.round( iCenterStart - (c+1)*vertAxisWidth2 ) );
- var iEnd = Math.round( iCenterStart - c*vertAxisWidth2 );
- leftAxes[ c ].viewport.setEdges( iStart, iEnd, jCenterStart, jCenterEnd );
- }
+ for (let c = 0; c < leftAxes.length; c++) {
+ const iStart = (c === leftAxes.length - 1 ? parentViewport.iStart : Math.round(iCenterStart - (c + 1) * vertAxisWidth2));
+ const iEnd = Math.round(iCenterStart - c * vertAxisWidth2);
+ leftAxes[c].viewport.setEdges(iStart, iEnd, jCenterStart, jCenterEnd);
+ }
- for ( var c = 0; c < rightAxes.length; c++ ) {
- var iStart = Math.round( iCenterEnd + c*vertAxisWidth2 );
- var iEnd = ( c === rightAxes.length-1 ? parentViewport.iEnd : Math.round( iCenterEnd + (c+1)*vertAxisWidth2 ) );
- rightAxes[ c ].viewport.setEdges( iStart, iEnd, jCenterStart, jCenterEnd );
- }
+ for (let c = 0; c < rightAxes.length; c++) {
+ const iStart = Math.round(iCenterEnd + c * vertAxisWidth2);
+ const iEnd = (c === rightAxes.length - 1 ? parentViewport.iEnd : Math.round(iCenterEnd + (c + 1) * vertAxisWidth2));
+ rightAxes[c].viewport.setEdges(iStart, iEnd, jCenterStart, jCenterEnd);
+ }
- for ( var c = 0; c < centers.length; c++ ) {
- centers[ c ].viewport.setEdges( iCenterStart, iCenterEnd, jCenterStart, jCenterEnd );
- }
+ for (let c = 0; c < centers.length; c++) {
+ centers[c].viewport.setEdges(iCenterStart, iCenterEnd, jCenterStart, jCenterEnd);
+ }
- for ( var c = 0; c < others.length; c++ ) {
- others[ c ].viewport.setEdges( 0, 0, 0, 0 );
- }
+ for (let c = 0; c < others.length; c++) {
+ others[c].viewport.setEdges(0, 0, 0, 0);
}
+ }
- };
- }
+ };
}
+
diff --git a/src/webglimpse/scroll.ts b/src/webglimpse/scroll.ts
index b0f10b9..338ffef 100644
--- a/src/webglimpse/scroll.ts
+++ b/src/webglimpse/scroll.ts
@@ -27,264 +27,284 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
-
- export interface VerticalScrollLayout extends Layout {
- jOffset : number;
- hContent : number;
- hVisible : number;
- }
-
+import {Layout, LayoutEntry, Drawable, Pane, Painter, PointerEvent, yFrac, isLeftMouseDown} from './core';
+import {Size, BoundsUnmodifiable} from './bounds';
+import {Color, gray} from './color';
+import {newBackgroundPainter, xyFrac_VERTSHADER, solid_FRAGSHADER, putQuadXys} from './misc';
+import {Program, UniformColor, Attribute} from './shader';
+import {newDynamicBuffer} from './buffer';
+import {GL, hasval} from './util/util';
+
+export interface VerticalScrollLayout extends Layout {
+ jOffset: number;
+ hContent: number;
+ hVisible: number;
+}
+
+
+export function newVerticalScrollLayout(): VerticalScrollLayout {
+ const layout = {
+
+ updatePrefSize: function (parentPrefSize: Size, children: LayoutEntry[]) {
+ if (children.length === 1) {
+ const childPrefSize = children[0].prefSize;
+
+ // XXX: Need some way to override the child's pref-height
+ if (!hasval(childPrefSize.h)) {
+ throw new Error('Vertical-scroll layout requires child to have a defined pref-height, but its pref-height is ' + childPrefSize.h);
+ }
- export function newVerticalScrollLayout( ) : VerticalScrollLayout {
- var layout = {
+ parentPrefSize.w = childPrefSize.w;
+ parentPrefSize.h = childPrefSize.h;
+ }
+ else if (children.length > 1) {
+ throw new Error('Vertical-scroll layout only works with 1 child, but pane has ' + this.children.length + ' children');
+ }
+ },
- updatePrefSize: function( parentPrefSize : Size, children : LayoutEntry[] ) {
- if ( children.length === 1 ) {
- var childPrefSize = children[ 0 ].prefSize;
+ jOffset: 0,
+ hContent: 0,
+ hVisible: 0,
- // XXX: Need some way to override the child's pref-height
- if ( !hasval( childPrefSize.h ) ) {
- throw new Error( 'Vertical-scroll layout requires child to have a defined pref-height, but its pref-height is ' + childPrefSize.h );
- }
+ updateChildViewports: function (children: LayoutEntry[], parentViewport: BoundsUnmodifiable) {
+ if (children.length === 1) {
+ const child = children[0];
- parentPrefSize.w = childPrefSize.w;
- parentPrefSize.h = childPrefSize.h;
+ let j;
+ const h = child.prefSize.h;
+ if (h <= parentViewport.h) {
+ j = parentViewport.jEnd - h;
}
- else if ( children.length > 1 ) {
- throw new Error( 'Vertical-scroll layout only works with 1 child, but pane has ' + this.children.length + ' children' );
+ else {
+ j = Math.min(parentViewport.j, parentViewport.jEnd - h + Math.max(0, Math.round(layout.jOffset)));
}
- },
- jOffset: 0,
- hContent: 0,
- hVisible: 0,
+ child.viewport.setRect(parentViewport.i, j, parentViewport.w, h);
- updateChildViewports: function( children : LayoutEntry[], parentViewport : BoundsUnmodifiable ) {
- if ( children.length === 1 ) {
- var child = children[ 0 ];
-
- var j;
- var h = child.prefSize.h;
- if ( h <= parentViewport.h ) {
- j = parentViewport.jEnd - h;
- }
- else {
- j = Math.min( parentViewport.j, parentViewport.jEnd - h + Math.max( 0, Math.round( layout.jOffset ) ) );
- }
-
- child.viewport.setRect( parentViewport.i, j, parentViewport.w, h );
-
- layout.jOffset = ( j + h ) - parentViewport.jEnd;
- layout.hContent = h;
- layout.hVisible = parentViewport.h;
- }
- else if ( children.length > 1 ) {
- throw new Error( 'Vertical-scroll layout only works with 1 child, but pane has ' + this.children.length + ' children' );
- }
+ layout.jOffset = (j + h) - parentViewport.jEnd;
+ layout.hContent = h;
+ layout.hVisible = parentViewport.h;
}
+ else if (children.length > 1) {
+ throw new Error('Vertical-scroll layout only works with 1 child, but pane has ' + this.children.length + ' children');
+ }
+ }
- };
- return layout;
- }
-
+ };
+ return layout;
+}
- export interface ScrollbarOptions {
- fgColor? : Color;
- bgColor? : Color;
- borderColor? : Color;
- borderThickness? : number;
- borderTop? : boolean;
- borderLeft? : boolean;
- borderRight? : boolean;
- borderBottom? : boolean;
- }
+export interface ScrollbarOptions {
+ fgColor?: Color;
+ bgColor?: Color;
+ borderColor?: Color;
+ borderThickness?: number;
+ borderTop?: boolean;
+ borderLeft?: boolean;
+ borderRight?: boolean;
+ borderBottom?: boolean;
+}
- export function newVerticalScrollbar( scrollLayout : VerticalScrollLayout, drawable : Drawable, options? : ScrollbarOptions ) : Pane {
- var bgColor = ( hasval( options ) && hasval( options.bgColor ) ? options.bgColor : gray( 0.9 ) );
- var scrollbar = new Pane( null );
- scrollbar.addPainter( newBackgroundPainter( bgColor ) );
- scrollbar.addPainter( newVerticalScrollbarPainter( scrollLayout, options ) );
+export function newVerticalScrollbar(scrollLayout: VerticalScrollLayout, drawable: Drawable, options?: ScrollbarOptions): Pane {
+ const bgColor = (hasval(options) && hasval(options.bgColor) ? options.bgColor : gray(0.9));
- attachVerticalScrollMouseListeners( scrollbar, scrollLayout, drawable );
+ const scrollbar = new Pane(null);
+ scrollbar.addPainter(newBackgroundPainter(bgColor));
+ scrollbar.addPainter(newVerticalScrollbarPainter(scrollLayout, options));
- return scrollbar;
- }
+ attachVerticalScrollMouseListeners(scrollbar, scrollLayout, drawable);
+ return scrollbar;
+}
- export function newVerticalScrollbarPainter( scrollLayout : VerticalScrollLayout, options? : ScrollbarOptions ) : Painter {
- var fgColor = ( hasval( options ) && hasval( options.fgColor ) ? options.fgColor : gray( 0.56 ) );
- var borderColor = ( hasval( options ) && hasval( options.borderColor ) ? options.borderColor : gray( 0.42 ) );
- var borderThickness = ( hasval( options ) && hasval( options.borderThickness ) ? options.borderThickness : 1 );
- var borderTop = ( hasval( options ) && hasval( options.borderTop ) ? options.borderTop : true );
- var borderLeft = ( hasval( options ) && hasval( options.borderLeft ) ? options.borderLeft : false );
- var borderRight = ( hasval( options ) && hasval( options.borderRight ) ? options.borderRight : false );
- var borderBottom = ( hasval( options ) && hasval( options.borderBottom ) ? options.borderBottom : true );
- var program = new Program( xyFrac_VERTSHADER, solid_FRAGSHADER );
- var u_Color = new UniformColor( program, 'u_Color' );
- var a_XyFrac = new Attribute( program, 'a_XyFrac' );
+export function newVerticalScrollbarPainter(scrollLayout: VerticalScrollLayout, options?: ScrollbarOptions): Painter {
+ const fgColor = (hasval(options) && hasval(options.fgColor) ? options.fgColor : gray(0.56));
+ const borderColor = (hasval(options) && hasval(options.borderColor) ? options.borderColor : gray(0.42));
+ const borderThickness = (hasval(options) && hasval(options.borderThickness) ? options.borderThickness : 1);
+ const borderTop = (hasval(options) && hasval(options.borderTop) ? options.borderTop : true);
+ const borderLeft = (hasval(options) && hasval(options.borderLeft) ? options.borderLeft : false);
+ const borderRight = (hasval(options) && hasval(options.borderRight) ? options.borderRight : false);
+ const borderBottom = (hasval(options) && hasval(options.borderBottom) ? options.borderBottom : true);
- var numFillVertices = 6;
- var numBorderVertices = ( borderTop ? 6 : 0 ) + ( borderLeft ? 6 : 0 ) + ( borderRight ? 6 : 0 ) + ( borderBottom ? 6 : 0 );
- var xyFrac = new Float32Array( 2 * Math.max( numFillVertices, numBorderVertices ) );
- var xyFracBuffer = newDynamicBuffer( );
+ const program = new Program(xyFrac_VERTSHADER, solid_FRAGSHADER);
+ const u_Color = new UniformColor(program, 'u_Color');
+ const a_XyFrac = new Attribute(program, 'a_XyFrac');
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- var hFrac = scrollLayout.hVisible / scrollLayout.hContent;
- if ( hFrac < 1 ) {
- var yTop = Math.round( ( ( scrollLayout.hContent - ( scrollLayout.jOffset ) ) / scrollLayout.hContent )*viewport.h + 1e-4 );
- var yBottom = Math.round( ( ( scrollLayout.hContent - ( scrollLayout.jOffset + scrollLayout.hVisible ) ) / scrollLayout.hContent )*viewport.h + 1e-4 );
+ const numFillVertices = 6;
+ const numBorderVertices = (borderTop ? 6 : 0) + (borderLeft ? 6 : 0) + (borderRight ? 6 : 0) + (borderBottom ? 6 : 0);
+ const xyFrac = new Float32Array(2 * Math.max(numFillVertices, numBorderVertices));
+ const xyFracBuffer = newDynamicBuffer();
- var yFracTop = yTop / viewport.h;
- var yFracBottom = yBottom / viewport.h;
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ const hFrac = scrollLayout.hVisible / scrollLayout.hContent;
+ if (hFrac < 1) {
+ const yTop = Math.round(((scrollLayout.hContent - (scrollLayout.jOffset)) / scrollLayout.hContent) * viewport.h + 1e-4);
+ const yBottom = Math.round(((scrollLayout.hContent - (scrollLayout.jOffset + scrollLayout.hVisible)) / scrollLayout.hContent) * viewport.h + 1e-4);
- var wBorderFrac = borderThickness / viewport.w;
- var hBorderFrac = borderThickness / viewport.h;
+ const yFracTop = yTop / viewport.h;
+ const yFracBottom = yBottom / viewport.h;
- gl.disable( GL.BLEND );
+ const wBorderFrac = borderThickness / viewport.w;
+ const hBorderFrac = borderThickness / viewport.h;
- program.use( gl );
+ gl.disable(GL.BLEND);
+ program.use(gl);
- // Fill
- //
- putQuadXys( xyFrac, 0, 0+(borderLeft?wBorderFrac:0), 1-(borderRight?wBorderFrac:0), yFracTop-(borderTop?hBorderFrac:0), yFracBottom+(borderBottom?hBorderFrac:0) );
- xyFracBuffer.setData( xyFrac.subarray( 0, 2*numFillVertices ) );
- a_XyFrac.setDataAndEnable( gl, xyFracBuffer, 2, GL.FLOAT );
- u_Color.setData( gl, fgColor );
- gl.drawArrays( GL.TRIANGLES, 0, numFillVertices );
+ // Fill
+ //
+ putQuadXys(xyFrac, 0, 0 + (borderLeft ? wBorderFrac : 0), 1 - (borderRight ? wBorderFrac : 0), yFracTop - (borderTop ? hBorderFrac : 0), yFracBottom + (borderBottom ? hBorderFrac : 0));
+ xyFracBuffer.setData(xyFrac.subarray(0, 2 * numFillVertices));
+ a_XyFrac.setDataAndEnable(gl, xyFracBuffer, 2, GL.FLOAT);
+ u_Color.setData(gl, fgColor);
+ gl.drawArrays(GL.TRIANGLES, 0, numFillVertices);
- // Border
- //
- var index = 0;
- if ( borderTop ) index = putQuadXys( xyFrac, index, 0, 1-(borderRight?wBorderFrac:0), yFracTop, yFracTop-hBorderFrac );
- if ( borderBottom ) index = putQuadXys( xyFrac, index, 0+(borderLeft?wBorderFrac:0), 1, yFracBottom+hBorderFrac, yFracBottom );
- if ( borderRight ) index = putQuadXys( xyFrac, index, 1-wBorderFrac, 1, yFracTop, yFracBottom+(borderBottom?hBorderFrac:0) );
- if ( borderLeft ) index = putQuadXys( xyFrac, index, 0, 0+wBorderFrac, yFracTop-(borderTop?hBorderFrac:0), yFracBottom );
+ // Border
+ //
- xyFracBuffer.setData( xyFrac.subarray( 0, 2*numBorderVertices ) );
- a_XyFrac.setDataAndEnable( gl, xyFracBuffer, 2, GL.FLOAT );
- u_Color.setData( gl, borderColor );
- gl.drawArrays( GL.TRIANGLES, 0, numBorderVertices );
-
-
- a_XyFrac.disable( gl );
- program.endUse( gl );
+ let index = 0;
+ if (borderTop) {
+ index = putQuadXys(xyFrac, index, 0, 1 - (borderRight ? wBorderFrac : 0), yFracTop, yFracTop - hBorderFrac);
}
- };
- }
-
- // mouse listener for scrolling while panning on the timeline itself
- export function attachTimelineVerticalScrollMouseListeners( pane : Pane, scrollLayout : VerticalScrollLayout, drawable : Drawable ) {
-
- // Used when dragging inside pane
- var grab : number = null;
- var jOffset : number = null;
-
- pane.mouseDown.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) ) {
- grab = ev.j;
- jOffset = scrollLayout.jOffset;
+ if (borderBottom) {
+ index = putQuadXys(xyFrac, index, 0 + (borderLeft ? wBorderFrac : 0), 1, yFracBottom + hBorderFrac, yFracBottom);
}
- } );
-
- pane.mouseMove.on( function( ev : PointerEvent ) {
- if ( hasval( grab ) ) {
- scrollLayout.jOffset = jOffset - ( grab - ev.j );
- drawable.redraw( );
+ if (borderRight) {
+ index = putQuadXys(xyFrac, index, 1 - wBorderFrac, 1, yFracTop, yFracBottom + (borderBottom ? hBorderFrac : 0));
}
- } );
-
- pane.mouseUp.on( function( ev : PointerEvent ) {
- grab = null;
- } );
- }
-
- // mouse listener for scrolling while interacting with the scrollbar
- export function attachVerticalScrollMouseListeners( scrollbar : Pane, scrollLayout : VerticalScrollLayout, drawable : Drawable ) {
-
- // Used when dragging the handle
- var grab : number = null;
-
- // Used when scrollbar is pressed-and-held outside the handle
- var pageScrollTimer : number = null;
- var recentPointerFrac : number = null;
-
- scrollbar.mouseDown.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) && !hasval( grab ) ) {
- var topFrac = ( scrollLayout.hContent - scrollLayout.jOffset ) / scrollLayout.hContent;
- var fracExtent = scrollLayout.hVisible / scrollLayout.hContent;
- var pointerFrac = yFrac( ev );
- if ( topFrac-fracExtent <= pointerFrac && pointerFrac <= topFrac ) {
- grab = ( topFrac - pointerFrac ) / fracExtent;
- }
- else {
- // If the mouse is pressed on the scrollbar but outside the handle:
- //
- // 1. Immediately scroll one page toward the mouse
- // 2. Wait 500ms
- // 3. If the mouse-button is still down, scroll one page toward the mouse
- // 4. Wait 50ms
- // 5. Go to Step 3
- //
- // (A "page" is 7/8 of the viewport extent.)
- //
-
- var direction = 0;
- if ( pointerFrac < topFrac-fracExtent ) direction = +1;
- else if ( pointerFrac > topFrac ) direction = -1;
- scrollLayout.jOffset += direction * Math.max( 1, 0.875 * scrollLayout.hVisible );
- drawable.redraw( );
-
- recentPointerFrac = pointerFrac;
- var pageScroll = function( ) {
- var topFrac = ( scrollLayout.hContent - scrollLayout.jOffset ) / scrollLayout.hContent;
- var fracExtent = scrollLayout.hVisible / scrollLayout.hContent;
- var pointerFrac = recentPointerFrac;
-
- var direction = 0;
- if ( pointerFrac < topFrac-fracExtent ) direction = +1;
- else if ( pointerFrac > topFrac ) direction = -1;
- scrollLayout.jOffset += direction * Math.max( 1, 0.875 * scrollLayout.hVisible );
- drawable.redraw( );
-
- pageScrollTimer = setTimeout( pageScroll, 50 );
- };
- pageScrollTimer = setTimeout( pageScroll, 500 );
- }
+ if (borderLeft) {
+ index = putQuadXys(xyFrac, index, 0, 0 + wBorderFrac, yFracTop - (borderTop ? hBorderFrac : 0), yFracBottom);
}
- } );
-
- scrollbar.mouseMove.on( function( ev : PointerEvent ) {
- var pointerFrac = yFrac( ev );
- if ( hasval( grab ) ) {
- var fracExtent = scrollLayout.hVisible / scrollLayout.hContent;
- var topFrac = pointerFrac + grab*fracExtent;
- scrollLayout.jOffset = scrollLayout.hContent - topFrac*scrollLayout.hContent;
- drawable.redraw( );
+
+ xyFracBuffer.setData(xyFrac.subarray(0, 2 * numBorderVertices));
+ a_XyFrac.setDataAndEnable(gl, xyFracBuffer, 2, GL.FLOAT);
+ u_Color.setData(gl, borderColor);
+ gl.drawArrays(GL.TRIANGLES, 0, numBorderVertices);
+
+
+ a_XyFrac.disable(gl);
+ program.endUse(gl);
+ }
+ };
+}
+
+// mouse listener for scrolling while panning on the timeline itself
+export function attachTimelineVerticalScrollMouseListeners(pane: Pane, scrollLayout: VerticalScrollLayout, drawable: Drawable) {
+
+ // Used when dragging inside pane
+ let grab: number = null;
+ let jOffset: number = null;
+
+ pane.mouseDown.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent)) {
+ grab = ev.j;
+ jOffset = scrollLayout.jOffset;
+ }
+ });
+
+ pane.mouseMove.on(function (ev: PointerEvent) {
+ if (hasval(grab)) {
+ scrollLayout.jOffset = jOffset - (grab - ev.j);
+ drawable.redraw();
+ }
+ });
+
+ pane.mouseUp.on(function (ev: PointerEvent) {
+ grab = null;
+ });
+}
+
+// mouse listener for scrolling while interacting with the scrollbar
+export function attachVerticalScrollMouseListeners(scrollbar: Pane, scrollLayout: VerticalScrollLayout, drawable: Drawable) {
+
+ // Used when dragging the handle
+ let grab: number = null;
+
+ // Used when scrollbar is pressed-and-held outside the handle
+ let pageScrollTimer: any = null;
+ let recentPointerFrac: number = null;
+
+ scrollbar.mouseDown.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent) && !hasval(grab)) {
+ const topFrac = (scrollLayout.hContent - scrollLayout.jOffset) / scrollLayout.hContent;
+ const fracExtent = scrollLayout.hVisible / scrollLayout.hContent;
+ const pointerFrac = yFrac(ev);
+ if (topFrac - fracExtent <= pointerFrac && pointerFrac <= topFrac) {
+ grab = (topFrac - pointerFrac) / fracExtent;
}
- if ( hasval( pageScrollTimer ) ) {
+ else {
+ // If the mouse is pressed on the scrollbar but outside the handle:
+ //
+ // 1. Immediately scroll one page toward the mouse
+ // 2. Wait 500ms
+ // 3. If the mouse-button is still down, scroll one page toward the mouse
+ // 4. Wait 50ms
+ // 5. Go to Step 3
+ //
+ // (A "page" is 7/8 of the viewport extent.)
+ //
+
+ let direction = 0;
+ if (pointerFrac < topFrac - fracExtent) {
+ direction = +1;
+ }
+ else if (pointerFrac > topFrac) {
+ direction = -1;
+ }
+ scrollLayout.jOffset += direction * Math.max(1, 0.875 * scrollLayout.hVisible);
+ drawable.redraw();
+
recentPointerFrac = pointerFrac;
- }
- } );
-
- scrollbar.mouseUp.on( function( ev : PointerEvent ) {
- grab = null;
- if ( hasval( pageScrollTimer ) ) {
- clearTimeout( pageScrollTimer );
- pageScrollTimer = null;
- recentPointerFrac = null;
- }
- } );
+ const pageScroll = function () {
+ const pageTopFrac = (scrollLayout.hContent - scrollLayout.jOffset) / scrollLayout.hContent;
+ const pageFracExtent = scrollLayout.hVisible / scrollLayout.hContent;
+ const pagePointerFrac = recentPointerFrac;
+
+ let pageDirection = 0;
+ if (pagePointerFrac < pageTopFrac - pageFracExtent) {
+ pageDirection = +1;
+ }
+ else if (pagePointerFrac > pageTopFrac) {
+ pageDirection = -1;
+ }
+ scrollLayout.jOffset += pageDirection * Math.max(1, 0.875 * scrollLayout.hVisible);
+ drawable.redraw();
- }
+ pageScrollTimer = setTimeout(pageScroll, 50);
+ };
+ pageScrollTimer = setTimeout(pageScroll, 500);
+ }
+ }
+ });
+
+ scrollbar.mouseMove.on(function (ev: PointerEvent) {
+ const pointerFrac = yFrac(ev);
+ if (hasval(grab)) {
+ const fracExtent = scrollLayout.hVisible / scrollLayout.hContent;
+ const topFrac = pointerFrac + grab * fracExtent;
+ scrollLayout.jOffset = scrollLayout.hContent - topFrac * scrollLayout.hContent;
+ drawable.redraw();
+ }
+ if (hasval(pageScrollTimer)) {
+ recentPointerFrac = pointerFrac;
+ }
+ });
+
+ scrollbar.mouseUp.on(function (ev: PointerEvent) {
+ grab = null;
+ if (hasval(pageScrollTimer)) {
+ clearTimeout(pageScrollTimer);
+ pageScrollTimer = null;
+ recentPointerFrac = null;
+ }
+ });
+
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/shader.ts b/src/webglimpse/shader.ts
index e5a5a48..50ec8df 100644
--- a/src/webglimpse/shader.ts
+++ b/src/webglimpse/shader.ts
@@ -27,304 +27,333 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
-
- function compileShader( gl : WebGLRenderingContext, shaderType : number, glsl : string ) : WebGLShader {
- var shader = gl.createShader( shaderType );
- gl.shaderSource( shader, glsl );
+import { GL, StringMap, getObjectId, hasval } from './util/util';
+import { Color } from './color';
+import { Texture } from './texture';
+import { Buffer } from './buffer';
+
+function compileShader(gl: WebGLRenderingContext, shaderType: number, glsl: string): WebGLShader {
+ const shader = gl.createShader(shaderType);
+ gl.shaderSource(shader, glsl);
+
+ gl.compileShader(shader);
+ if (!gl.getShaderParameter(shader, GL.COMPILE_STATUS)) {
+ throw new Error(gl.getShaderInfoLog(shader));
+ }
- gl.compileShader( shader );
- if ( !gl.getShaderParameter( shader, GL.COMPILE_STATUS ) ) throw new Error( gl.getShaderInfoLog( shader ) );
+ return shader;
+}
- return shader;
+function linkProgram(gl: WebGLRenderingContext, shaders: WebGLShader[]): WebGLProgram {
+ const program = gl.createProgram();
+ for (let i = 0; i < shaders.length; i++) {
+ gl.attachShader(program, shaders[i]);
}
-
- function linkProgram( gl : WebGLRenderingContext, shaders : WebGLShader[] ) : WebGLProgram {
- var program = gl.createProgram( );
- for ( var i = 0; i < shaders.length; i++ ) {
- gl.attachShader( program, shaders[ i ] );
- }
- try {
- gl.linkProgram( program );
- if ( !gl.getProgramParameter( program, GL.LINK_STATUS ) ) throw new Error( gl.getProgramInfoLog( program ) );
- return program;
- }
- finally {
- // It's good GL practice to detach and delete shaders after they have been linked
- // into a program, and are no longer needed. However, on some versions of Firefox,
- // doing so causes getAttribLocation and getUniformLocation to fail.
- //
- //for ( var i = 0; i < shaders.length; i++ ) {
- // gl.detachShader( program, shaders[ i ] );
- //}
+ try {
+ gl.linkProgram(program);
+ if (!gl.getProgramParameter(program, GL.LINK_STATUS)) {
+ throw new Error(gl.getProgramInfoLog(program));
}
+ return program;
}
-
- function createProgram( gl : WebGLRenderingContext, vertShaderSource : string, fragShaderSource : string ) : WebGLProgram {
- var shaders = [ ];
- try {
- shaders.push( compileShader( gl, GL.VERTEX_SHADER, vertShaderSource ) );
- shaders.push( compileShader( gl, GL.FRAGMENT_SHADER, fragShaderSource ) );
- return linkProgram( gl, shaders );
- }
- finally {
- // It's good GL practice to detach and delete shaders after they have been linked
- // into a program, and are no longer needed. However, on some versions of Firefox,
- // doing so causes getAttribLocation and getUniformLocation to fail.
- //
- //for ( var i = 0; i < shaders.length; i++ ) {
- // gl.deleteShader( shaders[ i ] );
- //}
- }
+ finally {
+ // It's good GL practice to detach and delete shaders after they have been linked
+ // into a program, and are no longer needed. However, on some versions of Firefox,
+ // doing so causes getAttribLocation and getUniformLocation to fail.
+ //
+ // for ( var i = 0; i < shaders.length; i++ ) {
+ // gl.detachShader( program, shaders[ i ] );
+ // }
+ }
+}
+
+function createProgram(gl: WebGLRenderingContext, vertShaderSource: string, fragShaderSource: string): WebGLProgram {
+ const shaders = [];
+ try {
+ shaders.push(compileShader(gl, GL.VERTEX_SHADER, vertShaderSource));
+ shaders.push(compileShader(gl, GL.FRAGMENT_SHADER, fragShaderSource));
+ return linkProgram(gl, shaders);
+ }
+ finally {
+ // It's good GL practice to detach and delete shaders after they have been linked
+ // into a program, and are no longer needed. However, on some versions of Firefox,
+ // doing so causes getAttribLocation and getUniformLocation to fail.
+ //
+ // for ( var i = 0; i < shaders.length; i++ ) {
+ // gl.deleteShader( shaders[ i ] );
+ // }
}
+}
- class ProgramEntry {
- gl : WebGLRenderingContext;
- program : WebGLProgram;
+class ProgramEntry {
+ gl: WebGLRenderingContext;
+ program: WebGLProgram;
- constructor( gl : WebGLRenderingContext, program : WebGLProgram ) {
- this.gl = gl;
- this.program = program;
- }
+ constructor(gl: WebGLRenderingContext, program: WebGLProgram) {
+ this.gl = gl;
+ this.program = program;
}
+}
- export class Program {
- private vertShaderSource : string;
- private fragShaderSource : string;
- private programs : StringMap = { };
+export class Program {
+ private vertShaderSource: string;
+ private fragShaderSource: string;
+ private programs: StringMap = {};
- constructor( vertShaderSource : string, fragShaderSource : string ) {
- this.vertShaderSource = vertShaderSource;
- this.fragShaderSource = fragShaderSource;
- }
+ constructor(vertShaderSource: string, fragShaderSource: string) {
+ this.vertShaderSource = vertShaderSource;
+ this.fragShaderSource = fragShaderSource;
+ }
- // XXX: Would be nice if this weren't public
- _program( gl : WebGLRenderingContext ) : WebGLProgram {
- var glId = getObjectId( gl );
- if ( this.programs[ glId ] === undefined ) {
- var program = createProgram( gl, this.vertShaderSource, this.fragShaderSource );
- this.programs[ glId ] = new ProgramEntry( gl, program );
- }
- return this.programs[ glId ].program;
+ // XXX: Would be nice if this weren't public
+ _program(gl: WebGLRenderingContext): WebGLProgram {
+ const glId = getObjectId(gl);
+ if (this.programs[glId] === undefined) {
+ const program = createProgram(gl, this.vertShaderSource, this.fragShaderSource);
+ this.programs[glId] = new ProgramEntry(gl, program);
}
+ return this.programs[glId].program;
+ }
- use( gl : WebGLRenderingContext ) {
- gl.useProgram( this._program( gl ) );
- }
+ use(gl: WebGLRenderingContext) {
+ gl.useProgram(this._program(gl));
+ }
- endUse( gl : WebGLRenderingContext ) {
- gl.useProgram( null );
- }
+ endUse(gl: WebGLRenderingContext) {
+ gl.useProgram(null);
+ }
- dispose( ) {
- // XXX: Not sure this actually works ... may have to make each gl current or something
- for ( var glid in this.programs ) {
- if ( this.programs.hasOwnProperty( glid ) ) {
- var en = this.programs[ glid ];
- en.gl.deleteProgram( en.program );
- }
+ dispose() {
+ // XXX: Not sure this actually works ... may have to make each gl current or something
+ for (const glid in this.programs) {
+ if (this.programs.hasOwnProperty(glid)) {
+ const en = this.programs[glid];
+ en.gl.deleteProgram(en.program);
}
- this.programs = { };
}
+ this.programs = {};
}
+}
- export class Uniform {
- private program : Program;
- private name : string;
- private optional : boolean;
- private locations : StringMap = { };
+export class Uniform {
+ private program: Program;
+ private name: string;
+ private optional: boolean;
+ private locations: StringMap = {};
- constructor( program : Program, name : string, optional : boolean ) {
- this.program = program;
- this.name = name;
- this.optional = optional;
- }
+ constructor(program: Program, name: string, optional: boolean) {
+ this.program = program;
+ this.name = name;
+ this.optional = optional;
+ }
- // XXX: Would be nice if this weren't public
- _location( gl : WebGLRenderingContext ) : WebGLUniformLocation {
- var glId = getObjectId( gl );
- if ( this.locations[ glId ] === undefined ) {
- var location = gl.getUniformLocation( this.program._program( gl ), this.name );
- if ( !this.optional && !hasval( location ) ) throw new Error( 'Uniform \'' + this.name + '\' not found' );
- this.locations[ glId ] = location;
+ // XXX: Would be nice if this weren't public
+ _location(gl: WebGLRenderingContext): WebGLUniformLocation {
+ const glId = getObjectId(gl);
+ if (this.locations[glId] === undefined) {
+ const location = gl.getUniformLocation(this.program._program(gl), this.name);
+ if (!this.optional && !hasval(location)) {
+ throw new Error('Uniform \'' + this.name + '\' not found');
}
- return this.locations[ glId ];
+ this.locations[glId] = location;
}
+ return this.locations[glId];
}
+}
- export class Uniform1f extends Uniform {
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+export class Uniform1f extends Uniform {
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setData( gl : WebGLRenderingContext, x : number ) {
- var location = this._location( gl );
- if ( hasval( location ) ) gl.uniform1f( location, x );
+ setData(gl: WebGLRenderingContext, x: number) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ gl.uniform1f(location, x);
}
}
+}
- export class Uniform2f extends Uniform {
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+export class Uniform2f extends Uniform {
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setData( gl : WebGLRenderingContext, x : number, y : number ) {
- var location = this._location( gl );
- if ( hasval( location ) ) gl.uniform2f( location, x, y );
+ setData(gl: WebGLRenderingContext, x: number, y: number) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ gl.uniform2f(location, x, y);
}
}
+}
- export class Uniform3f extends Uniform {
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+export class Uniform3f extends Uniform {
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setData( gl : WebGLRenderingContext, x : number, y : number, z : number ) {
- var location = this._location( gl );
- if ( hasval( location ) ) gl.uniform3f( location, x, y, z );
+ setData(gl: WebGLRenderingContext, x: number, y: number, z: number) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ gl.uniform3f(location, x, y, z);
}
}
+}
- export class Uniform4f extends Uniform {
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+export class Uniform4f extends Uniform {
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setData( gl : WebGLRenderingContext, x : number, y : number, z : number, w : number ) {
- var location = this._location( gl );
- if ( hasval( location ) ) gl.uniform4f( location, x, y, z, w );
+ setData(gl: WebGLRenderingContext, x: number, y: number, z: number, w: number) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ gl.uniform4f(location, x, y, z, w);
}
}
+}
- export class UniformMatrix4f extends Uniform {
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+export class UniformMatrix4f extends Uniform {
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setData( gl : WebGLRenderingContext, value : Float32Array, transpose : boolean = false ) {
- var location = this._location( gl );
- if ( hasval( location ) ) gl.uniformMatrix4fv( location, transpose, value );
+ setData(gl: WebGLRenderingContext, value: Float32Array, transpose: boolean = false) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ gl.uniformMatrix4fv(location, transpose, value);
}
}
+}
- export class Uniform1i extends Uniform {
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+export class Uniform1i extends Uniform {
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setData( gl : WebGLRenderingContext, x : number ) {
- var location = this._location( gl );
- if ( hasval( location ) ) gl.uniform1i( location, x );
+ setData(gl: WebGLRenderingContext, x: number) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ gl.uniform1i(location, x);
}
}
+}
- export class Uniform2i extends Uniform {
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+export class Uniform2i extends Uniform {
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setData( gl : WebGLRenderingContext, x : number, y : number ) {
- var location = this._location( gl );
- if ( hasval( location ) ) gl.uniform2i( location, x, y );
+ setData(gl: WebGLRenderingContext, x: number, y: number) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ gl.uniform2i(location, x, y);
}
}
+}
- export class Uniform3i extends Uniform {
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+export class Uniform3i extends Uniform {
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setData( gl : WebGLRenderingContext, x : number, y : number, z : number ) {
- var location = this._location( gl );
- if ( hasval( location ) ) gl.uniform3i( location, x, y, z );
+ setData(gl: WebGLRenderingContext, x: number, y: number, z: number) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ gl.uniform3i(location, x, y, z);
}
}
+}
- export class Uniform4i extends Uniform {
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+export class Uniform4i extends Uniform {
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setData( gl : WebGLRenderingContext, x : number, y : number, z : number, w : number ) {
- var location = this._location( gl );
- if ( hasval( location ) ) gl.uniform4i( location, x, y, z, w );
+ setData(gl: WebGLRenderingContext, x: number, y: number, z: number, w: number) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ gl.uniform4i(location, x, y, z, w);
}
}
+}
- export class UniformColor extends Uniform {
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+export class UniformColor extends Uniform {
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setData( gl : WebGLRenderingContext, color : Color ) {
- var location = this._location( gl );
- if ( hasval( location ) ) gl.uniform4f( location, color.r, color.g, color.b, color.a );
+ setData(gl: WebGLRenderingContext, color: Color) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ gl.uniform4f(location, color.r, color.g, color.b, color.a);
}
}
+}
- export class UniformSampler2D extends Uniform {
- private currentTexture : Texture;
+export class UniformSampler2D extends Uniform {
+ private currentTexture: Texture;
- constructor( program : Program, name : string, optional : boolean = false ) {
- super( program, name, optional );
- }
+ constructor(program: Program, name: string, optional: boolean = false) {
+ super(program, name, optional);
+ }
- setDataAndBind( gl : WebGLRenderingContext, textureUnit : number, texture : Texture ) {
- var location = this._location( gl );
- if ( hasval( location ) ) {
- texture.bind( gl, textureUnit );
- gl.uniform1i( location, textureUnit );
- this.currentTexture = texture;
- }
+ setDataAndBind(gl: WebGLRenderingContext, textureUnit: number, texture: Texture) {
+ const location = this._location(gl);
+ if (hasval(location)) {
+ texture.bind(gl, textureUnit);
+ gl.uniform1i(location, textureUnit);
+ this.currentTexture = texture;
}
+ }
- unbind( gl : WebGLRenderingContext ) {
- if ( hasval( this.currentTexture ) ) {
- this.currentTexture.unbind( gl );
- this.currentTexture = null;
- }
+ unbind(gl: WebGLRenderingContext) {
+ if (hasval(this.currentTexture)) {
+ this.currentTexture.unbind(gl);
+ this.currentTexture = null;
}
}
+}
- export class Attribute {
- private program : Program;
- private name : string;
- private locations : StringMap = { };
+export class Attribute {
+ private program: Program;
+ private name: string;
+ private locations: StringMap = {};
- constructor( program : Program, name : string ) {
- this.program = program;
- this.name = name;
- }
+ constructor(program: Program, name: string) {
+ this.program = program;
+ this.name = name;
+ }
- // XXX: Would be nice if this weren't public
- _location( gl : WebGLRenderingContext ) : number {
- var glId = getObjectId( gl );
- if ( this.locations[ glId ] === undefined ) {
- var location = gl.getAttribLocation( this.program._program( gl ), this.name );
- if ( location === -1 ) throw new Error( 'Attribute "' + this.name + '" not found' );
- this.locations[ glId ] = location;
+ // XXX: Would be nice if this weren't public
+ _location(gl: WebGLRenderingContext): number {
+ const glId = getObjectId(gl);
+ if (this.locations[glId] === undefined) {
+ const location = gl.getAttribLocation(this.program._program(gl), this.name);
+ if (location === -1) {
+ throw new Error('Attribute "' + this.name + '" not found');
}
- return this.locations[ glId ];
+ this.locations[glId] = location;
}
+ return this.locations[glId];
+ }
- setDataAndEnable( gl : WebGLRenderingContext, buffer : Buffer, size : number, type : number, normalized : boolean = false, stride : number = 0, offset : number = 0 ) {
- var location = this._location( gl );
- gl.enableVertexAttribArray( location );
- buffer.bind( gl, GL.ARRAY_BUFFER );
- gl.vertexAttribPointer( location, size, type, normalized, stride, offset );
- buffer.unbind( gl, GL.ARRAY_BUFFER );
- }
+ setDataAndEnable(gl: WebGLRenderingContext, buffer: Buffer, size: number, type: number, normalized: boolean = false, stride: number = 0, offset: number = 0) {
+ const location = this._location(gl);
+ gl.enableVertexAttribArray(location);
+ buffer.bind(gl, GL.ARRAY_BUFFER);
+ gl.vertexAttribPointer(location, size, type, normalized, stride, offset);
+ buffer.unbind(gl, GL.ARRAY_BUFFER);
+ }
- disable( gl : WebGLRenderingContext ) {
- gl.disableVertexAttribArray( this._location( gl ) );
- }
+ disable(gl: WebGLRenderingContext) {
+ gl.disableVertexAttribArray(this._location(gl));
}
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/text.ts b/src/webglimpse/text.ts
index d3986e4..d4ebeb8 100644
--- a/src/webglimpse/text.ts
+++ b/src/webglimpse/text.ts
@@ -27,501 +27,568 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Cache } from './util/cache';
+import { Texture2D, ImageDrawer } from './texture';
+import { Color, black, parseRgba } from './color';
+import { TwoKeyCache, ThreeKeyCache, SixKeyCache } from './util/multikey_cache';
+import { GL, concatLines, hasval } from './util/util';
+import { Program, Uniform2f, Uniform1f, UniformSampler2D, Attribute } from './shader';
+import { Buffer, newStaticBuffer } from './buffer';
+import { BoundsUnmodifiable } from './bounds';
+import { nearestPixel } from './misc';
+
+interface Dim2D {
+ w: number;
+ h: number;
+}
+
+
+const textDim = (function () {
+ // Use div to figure out how big our texture needs to be
+ const div = document.createElement('div');
+ div.style.setProperty('position', 'absolute');
+ div.style.setProperty('padding', '0');
+ div.style.setProperty('margin', '0');
+ div.style.setProperty('width', 'auto');
+ div.style.setProperty('height', 'auto');
+ div.style.setProperty('visibility', 'hidden');
+
+ return function (s: string, font: string): Dim2D {
+ div.style.setProperty('font', font);
+ div.textContent = s;
+
+ document.body.appendChild(div);
+ const width = div.clientWidth;
+ const height = div.clientHeight;
+ document.body.removeChild(div);
+
+ return { w: width, h: height };
+ };
+})();
+
+
+
+interface FontMetrics {
+ jTop: number;
+ jBaseline: number;
+ jBottom: number;
+}
+
+
+function newFontMetricsCache(): Cache {
+ return new Cache({
+ create: function (font: string): FontMetrics {
+
+ const dim = textDim('fMgyj', font);
+ const w = dim.w;
+ const h = dim.h;
+ const canvas = document.createElement('canvas');
+ canvas.width = w;
+ canvas.height = h;
+ const g = canvas.getContext('2d', { willReadFrequently: true});
-
- interface Dim2D {
- w : number;
- h : number;
- }
-
-
- var textDim = ( function( ) {
- // Use div to figure out how big our texture needs to be
- var div = document.createElement( 'div' );
- div.style.setProperty( 'position', 'absolute' );
- div.style.setProperty( 'padding', '0' );
- div.style.setProperty( 'margin', '0' );
- div.style.setProperty( 'width', 'auto' );
- div.style.setProperty( 'height', 'auto' );
- div.style.setProperty( 'visibility', 'hidden' );
-
- return function( s : string, font : string ) : Dim2D {
- div.style.setProperty( 'font', font );
- div.textContent = s;
-
- document.body.appendChild( div );
- var width = div.clientWidth;
- var height = div.clientHeight;
- document.body.removeChild( div );
-
- return { w: width, h: height };
- };
- } )( );
-
-
-
- interface FontMetrics {
- jTop : number;
- jBaseline : number;
- jBottom : number;
- }
-
-
- function newFontMetricsCache( ) : Cache {
- return new Cache( {
- create: function( font : string ) : FontMetrics {
-
- var dim = textDim( 'fMgyj', font );
- var w = dim.w;
- var h = dim.h;
- var canvas = document.createElement( 'canvas' );
- canvas.width = w;
- canvas.height = h;
- var g = canvas.getContext( '2d' );
-
- g.font = font;
- g.textAlign = 'left';
- g.textBaseline = 'top';
- g.fillStyle = 'black';
-
- g.clearRect( 0, 0, w, h );
- g.fillText( 'fM', 0, 0 );
- var rgbaData = g.getImageData( 0, 0, w, h ).data;
-
- var jTop = -1;
- for ( var j = 0; j < h && jTop < 0; j++ ) {
- for ( var i = 0; i < w && jTop < 0; i++ ) {
- var alpha = rgbaData[ ( j*w + i )*4 + 3 ];
- if ( alpha !== 0 ) jTop = j;
+ g.font = font;
+ g.textAlign = 'left';
+ g.textBaseline = 'top';
+ g.fillStyle = 'black';
+
+ g.clearRect(0, 0, w, h);
+ g.fillText('fM', 0, 0);
+ let rgbaData = g.getImageData(0, 0, w, h).data;
+
+ let jTop = -1;
+ for (let j = 0; j < h && jTop < 0; j++) {
+ for (let i = 0; i < w && jTop < 0; i++) {
+ const alpha = rgbaData[(j * w + i) * 4 + 3];
+ if (alpha !== 0) {
+ jTop = j;
}
}
+ }
- var jBaseline = -1;
- for ( var j = h-1; j >= 0 && jBaseline < 0; j-- ) {
- for ( var i = 0; i < w && jBaseline < 0; i++ ) {
- var alpha = rgbaData[ ( j*w + i )*4 + 3 ];
- if ( alpha !== 0 ) jBaseline = j;
+ let jBaseline = -1;
+ for (let j = h - 1; j >= 0 && jBaseline < 0; j--) {
+ for (let i = 0; i < w && jBaseline < 0; i++) {
+ const alpha = rgbaData[(j * w + i) * 4 + 3];
+ if (alpha !== 0) {
+ jBaseline = j;
}
}
+ }
- g.clearRect( 0, 0, w, h );
- g.fillText( 'gyj', 0, 0 );
- var rgbaData = g.getImageData( 0, 0, w, h ).data;
+ g.clearRect(0, 0, w, h);
+ g.fillText('gyj', 0, 0);
+ rgbaData = g.getImageData(0, 0, w, h).data;
- var jBottom = -1;
- for ( var j = h-1; j >= 0 && jBottom < 0; j-- ) {
- for ( var i = 0; i < w && jBottom < 0; i++ ) {
- var alpha = rgbaData[ ( j*w + i )*4 + 3 ];
- if ( alpha !== 0 ) jBottom = j;
+ let jBottom = -1;
+ for (let j = h - 1; j >= 0 && jBottom < 0; j--) {
+ for (let i = 0; i < w && jBottom < 0; i++) {
+ const alpha = rgbaData[(j * w + i) * 4 + 3];
+ if (alpha !== 0) {
+ jBottom = j;
}
}
+ }
- return { jTop: jTop, jBaseline: jBaseline, jBottom: jBottom };
- },
- dispose: function( ) { }
- } );
- }
-
-
- var getRawFontMetrics = ( function( ) {
- var cache = newFontMetricsCache( );
- return function( font : string ) : FontMetrics {
- return cache.value( font );
- };
- } )( );
-
-
- var getTextWidth = ( function( ) {
- var canvas = document.createElement( 'canvas' );
- canvas.width = 1;
- canvas.height = 1;
- var g = canvas.getContext( '2d' );
- g.textAlign = 'left';
- g.textBaseline = 'top';
- return function( font : string, text : string ) : number {
- g.font = font;
- return g.measureText( text ).width;
- };
- } )( );
-
-
- export class TextTexture2D extends Texture2D {
- private _jBaseline : number;
-
- get jBaseline( ) : number { return this._jBaseline; }
+ return { jTop: jTop, jBaseline: jBaseline, jBottom: jBottom };
+ },
+ dispose: function () { }
+ });
+}
- constructor( w : number, h : number, jBaseline : number, minFilter : number, magFilter : number, draw : ImageDrawer ) {
- super( w, h, minFilter, magFilter, draw );
- this._jBaseline = jBaseline;
- }
- yAnchor( textFrac : number ) {
- // Things tend to look the way you expect if textFrac is interpreted as
- // a fraction of the way from the bottom of the baseline pixel up to the
- // top of the top pixel
- //
- var bottom = this.jBaseline + 1;
- var h = this.h;
- return 1 - ( ( 1 - textFrac )*bottom / h );
- }
- }
+const getRawFontMetrics = (function () {
+ const cache = newFontMetricsCache();
+ return function (font: string): FontMetrics {
+ return cache.value(font);
+ };
+})();
- export function newTextTextureCache( font : string, color : Color ) : Cache {
- var createTextTexture = createTextTextureFactory( font );
- return new Cache( {
- create: function( text : string ) : TextTexture2D {
- return createTextTexture( color, text );
- },
- dispose: function( texture : Texture2D ) {
- texture.dispose( );
- }
- } );
- }
+export const getTextWidth = (function () {
+ const canvas = document.createElement('canvas');
+ canvas.width = 1;
+ canvas.height = 1;
+ const g = canvas.getContext('2d');
+ g.textAlign = 'left';
+ g.textBaseline = 'top';
+ return function (font: string, text: string): number {
+ g.font = font;
+ return g.measureText(text).width;
+ };
+})();
- export function newTextTextureCache2( font : string ) : TwoKeyCache {
- var createTextTexture = createTextTextureFactory( font );
- return new TwoKeyCache( {
- create: function( rgbaString : string, text : string ) : TextTexture2D {
- var color = parseRgba( rgbaString );
- return createTextTexture( color, text );
- },
- dispose: function( texture : Texture2D ) {
- texture.dispose( );
- }
- } );
- }
+export class TextTexture2D extends Texture2D {
+ private _jBaseline: number;
+ get jBaseline(): number { return this._jBaseline; }
- export function newTextTextureCache3( ) : ThreeKeyCache {
- return new ThreeKeyCache( {
- create: function( font : string, rgbaString : string, text : string ) : TextTexture2D {
- var createTextTexture = createTextTextureFactory( font );
- var color = parseRgba( rgbaString );
- return createTextTexture( color, text );
- },
- dispose: function( texture : Texture2D ) {
- texture.dispose( );
- }
- } );
+ constructor(w: number, h: number, jBaseline: number, minFilter: number, magFilter: number, draw: ImageDrawer) {
+ super(w, h, minFilter, magFilter, draw);
+ this._jBaseline = jBaseline;
}
-
- export interface TextTextureFactory {
- ( color : Color, text : string ) : TextTexture2D;
+ yAnchor(textFrac: number) {
+ // Things tend to look the way you expect if textFrac is interpreted as
+ // a fraction of the way from the bottom of the baseline pixel up to the
+ // top of the top pixel
+ //
+ const bottom = this.jBaseline + 1;
+ const h = this.h;
+ return 1 - ((1 - textFrac) * bottom / h);
}
+}
- export function createTextTextureFactory( font : string ) : TextTextureFactory {
- var rawFontMetrics = getRawFontMetrics( font );
- var jBaseline = rawFontMetrics.jBaseline - rawFontMetrics.jTop;
- var h = rawFontMetrics.jBottom - rawFontMetrics.jTop + 1;
+export function newTextTextureCache(font: string, color: Color): Cache {
+ const createTextTexture = createTextTextureFactory(font);
+ return new Cache({
+ create: function (text: string): TextTexture2D {
+ return createTextTexture(color, text);
+ },
+ dispose: function (texture: Texture2D) {
+ texture.dispose();
+ }
+ });
+}
+
+
+export function newTextTextureCache2(font: string): TwoKeyCache {
+ const createTextTexture = createTextTextureFactory(font);
+ return new TwoKeyCache({
+ create: function (rgbaString: string, text: string): TextTexture2D {
+ const color = parseRgba(rgbaString);
+ return createTextTexture(color, text);
+ },
+ dispose: function (texture: Texture2D) {
+ texture.dispose();
+ }
+ });
+}
+
+
+export function newTextTextureCache3(): ThreeKeyCache {
+ return new ThreeKeyCache({
+ create: function (font: string, rgbaString: string, text: string): TextTexture2D {
+ const createTextTexture = createTextTextureFactory(font);
+ const color = parseRgba(rgbaString);
+ return createTextTexture(color, text);
+ },
+ dispose: function (texture: Texture2D) {
+ texture.dispose();
+ }
+ });
+}
+
+export function newTextTextureCache6(): SixKeyCache {
+ return new SixKeyCache({
+ create: function (font: string, rgbaString: string, text: string, rgbaBgString: string, bgPadding: string, bgBorderRadius: string): TextTexture2D {
+ const createTextTexture = createTextTextureFactory(font);
+ const color = parseRgba(rgbaString);
+ const bgColor = parseRgba(rgbaBgString);
+ const bgBorderRadiusNumber = parseInt(bgBorderRadius, 10);
+ const paddingNumber = parseInt(bgPadding, 10);
+ return createTextTexture(color, text, bgColor, paddingNumber, bgBorderRadiusNumber);
+ },
+ dispose: function (texture: Texture2D) {
+ texture.dispose();
+ }
+ });
+}
+
+
+export type TextTextureFactory = (
+ color: Color,
+ text: string,
+ bgColor?: Color,
+ bgPadding?: number,
+ bgBorderRadius?: number
+) => TextTexture2D;
+
+
+export function createTextTextureFactory(font: string): TextTextureFactory {
+ const rawFontMetrics = getRawFontMetrics(font);
+ let jBaseline = rawFontMetrics.jBaseline - rawFontMetrics.jTop;
+ let h = rawFontMetrics.jBottom - rawFontMetrics.jTop + 1;
+
+ return function (color: Color, text: string, bgColor: Color, bgPadding = 0, bgBorderRadius = 0): TextTexture2D {
+ let w = Math.ceil(getTextWidth(font, text));
+ if (bgColor && bgColor.a !== 0) {
+ const addedPadding = bgPadding * 2;
+ w = w + addedPadding + 4;
+ h = h + addedPadding;
+ jBaseline = jBaseline + addedPadding;
+ }
- return function( color : Color, text : string ) : TextTexture2D {
- var w = getTextWidth( font, text );
+ return new TextTexture2D(w, h, jBaseline, GL.NEAREST, GL.NEAREST, function (g: CanvasRenderingContext2D) {
+ // Some browsers use hinting for canvas fillText! This behaves poorly on a transparent
+ // background -- so we draw white text onto a black background, then infer alpha from
+ // the pixel color (black = transparent, white = opaque).
+ //
+ // We compute alpha as (R+G+B)/3. This grayscales the image the browser drew, effectively
+ // de-hinting it.
+ //
- return new TextTexture2D( w, h, jBaseline, GL.NEAREST, GL.NEAREST, function( g : CanvasRenderingContext2D ) {
- // Some browsers use hinting for canvas fillText! This behaves poorly on a transparent
- // background -- so we draw white text onto a black background, then infer alpha from
- // the pixel color (black = transparent, white = opaque).
- //
- // We compute alpha as (R+G+B)/3. This grayscales the image the browser drew, effectively
- // de-hinting it.
- //
+ // If background color not transparent show background color, else follow comment above.
+ if (bgColor && bgColor.a !== 0) {
+ g.fillStyle = bgColor.cssString;
+ // Round corners of text background
+ if (bgBorderRadius) {
+ bgBorderRadius = Math.min(bgBorderRadius, h / 2, w / 2);
+ g.beginPath();
+ g.moveTo(bgBorderRadius, 0);
+ g.lineTo(w - bgBorderRadius, 0);
+ g.quadraticCurveTo(w, 0, w, bgBorderRadius);
+ g.lineTo(w, h - bgBorderRadius);
+ g.quadraticCurveTo(w, h, w - bgBorderRadius, h);
+ g.lineTo(bgBorderRadius, h);
+ g.quadraticCurveTo(0, h, 0, h - bgBorderRadius);
+ g.lineTo(0, bgBorderRadius);
+ g.quadraticCurveTo(0, 0, bgBorderRadius, 0);
+ g.closePath();
+ g.fill();
+ } else {
+ g.fillRect(0, 0, w, h);
+ }
+ g.font = font;
+ g.textAlign = 'center';
+ g.textBaseline = 'middle';
+ g.fillStyle = color.cssString;
+ g.fillText(text, w / 2, h / 2);
+ } else {
g.fillStyle = 'black';
- g.fillRect( 0, 0, w, h );
+ g.fillRect(0, 0, w, h);
g.font = font;
g.textAlign = 'left';
g.textBaseline = 'top';
g.fillStyle = 'white';
- g.save( );
- g.translate( 0, -rawFontMetrics.jTop );
- g.fillText( text, 0, 0 );
- g.restore( );
-
- var r255 = 255 * color.r;
- var g255 = 255 * color.g;
- var b255 = 255 * color.b;
- var aFactor = color.a / 3;
-
- var pixels = g.getImageData( 0, 0, w, h );
- for ( var j = 0; j < pixels.height; j++ ) {
- for ( var i = 0; i < pixels.width; i++ ) {
- var pixelOffset = ( j*pixels.width + i )*4;
- var a255 = aFactor * ( pixels.data[pixelOffset+0] + pixels.data[pixelOffset+1] + pixels.data[pixelOffset+2] );
-
- pixels.data[ pixelOffset+0 ] = r255;
- pixels.data[ pixelOffset+1 ] = g255;
- pixels.data[ pixelOffset+2 ] = b255;
- pixels.data[ pixelOffset+3 ] = a255;
+ g.save();
+ g.translate(0, -rawFontMetrics.jTop);
+ g.fillText(text, 0, 0);
+ g.restore();
+
+ const r255 = 255 * color.r;
+ const g255 = 255 * color.g;
+ const b255 = 255 * color.b;
+ const aFactor = color.a / 3;
+
+ const pixels = g.getImageData(0, 0, w, h);
+ for (let j = 0; j < pixels.height; j++) {
+ for (let i = 0; i < pixels.width; i++) {
+ const pixelOffset = (j * pixels.width + i) * 4;
+ const a255 = aFactor * (pixels.data[pixelOffset + 0] + pixels.data[pixelOffset + 1] + pixels.data[pixelOffset + 2]);
+
+ pixels.data[pixelOffset + 0] = r255;
+ pixels.data[pixelOffset + 1] = g255;
+ pixels.data[pixelOffset + 2] = b255;
+ pixels.data[pixelOffset + 3] = a255;
}
}
- g.putImageData( pixels, 0, 0 );
- } );
- }
- }
-
-
-
-
-
-
-
-
- export function newTextHintsCache( font : string ) : Cache {
- var rawFontMetrics = getRawFontMetrics( font );
- var jBaseline = rawFontMetrics.jBaseline - rawFontMetrics.jTop;
- var h = rawFontMetrics.jBottom - rawFontMetrics.jTop + 1;
-
- return new Cache( {
-
- create: function( text : string ) : Texture2D {
- var w = getTextWidth( font, text );
-
- // XXX: For now, assuming subpixels are horizontal-RGB
-
- // Draw text triple-sized, to get an alpha for each r,g,b subpixel
- var canvas3 = document.createElement( 'canvas' );
- canvas3.width = 3 * w;
- canvas3.height = h;
- var g3 = canvas3.getContext( '2d' );
- g3.fillStyle = 'black';
- g3.fillRect( 0, 0, canvas3.width, canvas3.height );
-
- g3.save( );
- g3.translate( 0, -rawFontMetrics.jTop );
- g3.scale( 3, 1 );
- g3.font = font;
- g3.textAlign = 'left';
- g3.textBaseline = 'top';
- g3.fillStyle = 'white';
- g3.fillText( text, 0, 0 );
- g3.restore( );
- var srcRgba = g3.getImageData( 0, 0, canvas3.width, canvas3.height ).data;
-
- return new Texture2D( w, h, GL.NEAREST, GL.NEAREST, function( g : CanvasRenderingContext2D ) {
- var destImage = g.createImageData( w, h );
- var destRgba = destImage.data;
-
- var weightLeft = 1;
- var weightCenter = 2;
- var weightRight = 1;
- var weightNorm = 1 / ( weightLeft + weightCenter + weightRight );
-
- for ( var j = 0; j < h; j++ ) {
- for ( var i = 0; i < w; i++ ) {
- // Get alpha values for relevant src-pixels: one from just left of the dest-pixel, all
- // three from inside the dest-pixel, and one from just right of the dest-pixel.
- //
- // Some browsers use hinting for canvas fillText! This behaves poorly on a transparent
- // background -- so we draw white text onto a black background, then infer alpha from
- // the pixel color (black = transparent, white = opaque).
- //
- // We compute alpha as (R+G+B)/3. This grayscales the image the browser drew, effectively
- // de-hinting it so that we can re-hint it ourselves later (during blending, when the
- // background color is known).
- //
- var srcPixelIndex = ( j*3*w + 3*i )*4;
- var srcAlphaL = ( i > 0 ? ( srcRgba[srcPixelIndex-4] + srcRgba[srcPixelIndex-3] + srcRgba[srcPixelIndex-2] ) / ( 3 * 255 ) : 0 );
- var srcAlpha0 = ( srcRgba[srcPixelIndex+0] + srcRgba[srcPixelIndex+1] + srcRgba[srcPixelIndex+2] ) / ( 3 * 255 );
- var srcAlpha1 = ( srcRgba[srcPixelIndex+4] + srcRgba[srcPixelIndex+5] + srcRgba[srcPixelIndex+6] ) / ( 3 * 255 );
- var srcAlpha2 = ( srcRgba[srcPixelIndex+8] + srcRgba[srcPixelIndex+9] + srcRgba[srcPixelIndex+10] ) / ( 3 * 255 );
- var srcAlphaR = ( i < w-1 ? ( srcRgba[srcPixelIndex+12] + srcRgba[srcPixelIndex+13] + srcRgba[srcPixelIndex+14] ) / ( 3 * 255 ) : 0 );
-
- // Weighted averages to find subpixel alphas
- var alphaLeft = weightNorm*( weightLeft*srcAlphaL + weightCenter*srcAlpha0 + weightRight*srcAlpha1 );
- var alphaCenter = weightNorm*( weightLeft*srcAlpha0 + weightCenter*srcAlpha1 + weightRight*srcAlpha2 );
- var alphaRight = weightNorm*( weightLeft*srcAlpha1 + weightCenter*srcAlpha2 + weightRight*srcAlphaR );
-
- // Store subpixel alphas in dest-pixel
- var destPixelIndex = ( j*w + i )*4;
- destRgba[ destPixelIndex + 0 ] = Math.round( 255 * alphaLeft );
- destRgba[ destPixelIndex + 1 ] = Math.round( 255 * alphaCenter );
- destRgba[ destPixelIndex + 2 ] = Math.round( 255 * alphaRight );
-
- // Alpha will be used in computing final alpha of blended result -- use the average of
- // the subpixel alphas
- //
- // If alpha is 1, Firefox will interpret it as 100%. Causes some pixels that should be
- // very dim to come out very bright. As a workaround, nudge them down to zero.
- //
- var alphaAvg = Math.round( 255 * ( alphaLeft + alphaCenter + alphaRight ) / 3 );
- destRgba[ destPixelIndex + 3 ] = ( alphaAvg === 1 ? 0 : alphaAvg );
- }
- }
-
- g.putImageData( destImage, 0, 0 );
- } );
- },
-
- dispose: function( texture : Texture2D ) {
- texture.dispose( );
+ g.putImageData(pixels, 0, 0);
}
+ });
+ };
+}
+
+
+
+
+
+
+
+
+export function newTextHintsCache(font: string): Cache {
+ const rawFontMetrics = getRawFontMetrics(font);
+ const jBaseline = rawFontMetrics.jBaseline - rawFontMetrics.jTop;
+ const h = rawFontMetrics.jBottom - rawFontMetrics.jTop + 1;
+
+ return new Cache({
+
+ create: function (text: string): Texture2D {
+ const w = getTextWidth(font, text);
+
+ // XXX: For now, assuming subpixels are horizontal-RGB
+
+ // Draw text triple-sized, to get an alpha for each r,g,b subpixel
+ const canvas3 = document.createElement('canvas');
+ canvas3.width = 3 * w;
+ canvas3.height = h;
+ const g3 = canvas3.getContext('2d');
+ g3.fillStyle = 'black';
+ g3.fillRect(0, 0, canvas3.width, canvas3.height);
+
+ g3.save();
+ g3.translate(0, -rawFontMetrics.jTop);
+ g3.scale(3, 1);
+ g3.font = font;
+ g3.textAlign = 'left';
+ g3.textBaseline = 'top';
+ g3.fillStyle = 'white';
+ g3.fillText(text, 0, 0);
+ g3.restore();
+ const srcRgba = g3.getImageData(0, 0, canvas3.width, canvas3.height).data;
+
+ return new Texture2D(w, h, GL.NEAREST, GL.NEAREST, function (g: CanvasRenderingContext2D) {
+ const destImage = g.createImageData(w, h);
+ const destRgba = destImage.data;
+
+ const weightLeft = 1;
+ const weightCenter = 2;
+ const weightRight = 1;
+ const weightNorm = 1 / (weightLeft + weightCenter + weightRight);
+
+ for (let j = 0; j < h; j++) {
+ for (let i = 0; i < w; i++) {
+ // Get alpha values for relevant src-pixels: one from just left of the dest-pixel, all
+ // three from inside the dest-pixel, and one from just right of the dest-pixel.
+ //
+ // Some browsers use hinting for canvas fillText! This behaves poorly on a transparent
+ // background -- so we draw white text onto a black background, then infer alpha from
+ // the pixel color (black = transparent, white = opaque).
+ //
+ // We compute alpha as (R+G+B)/3. This grayscales the image the browser drew, effectively
+ // de-hinting it so that we can re-hint it ourselves later (during blending, when the
+ // background color is known).
+ //
+ const srcPixelIndex = (j * 3 * w + 3 * i) * 4;
+ const srcAlphaL = (i > 0 ? (srcRgba[srcPixelIndex - 4] + srcRgba[srcPixelIndex - 3] + srcRgba[srcPixelIndex - 2]) / (3 * 255) : 0);
+ const srcAlpha0 = (srcRgba[srcPixelIndex + 0] + srcRgba[srcPixelIndex + 1] + srcRgba[srcPixelIndex + 2]) / (3 * 255);
+ const srcAlpha1 = (srcRgba[srcPixelIndex + 4] + srcRgba[srcPixelIndex + 5] + srcRgba[srcPixelIndex + 6]) / (3 * 255);
+ const srcAlpha2 = (srcRgba[srcPixelIndex + 8] + srcRgba[srcPixelIndex + 9] + srcRgba[srcPixelIndex + 10]) / (3 * 255);
+ const srcAlphaR = (i < w - 1 ? (srcRgba[srcPixelIndex + 12] + srcRgba[srcPixelIndex + 13] + srcRgba[srcPixelIndex + 14]) / (3 * 255) : 0);
+
+ // Weighted averages to find subpixel alphas
+ const alphaLeft = weightNorm * (weightLeft * srcAlphaL + weightCenter * srcAlpha0 + weightRight * srcAlpha1);
+ const alphaCenter = weightNorm * (weightLeft * srcAlpha0 + weightCenter * srcAlpha1 + weightRight * srcAlpha2);
+ const alphaRight = weightNorm * (weightLeft * srcAlpha1 + weightCenter * srcAlpha2 + weightRight * srcAlphaR);
+
+ // Store subpixel alphas in dest-pixel
+ const destPixelIndex = (j * w + i) * 4;
+ destRgba[destPixelIndex + 0] = Math.round(255 * alphaLeft);
+ destRgba[destPixelIndex + 1] = Math.round(255 * alphaCenter);
+ destRgba[destPixelIndex + 2] = Math.round(255 * alphaRight);
+
+ // Alpha will be used in computing final alpha of blended result -- use the average of
+ // the subpixel alphas
+ //
+ // If alpha is 1, Firefox will interpret it as 100%. Causes some pixels that should be
+ // very dim to come out very bright. As a workaround, nudge them down to zero.
+ //
+ const alphaAvg = Math.round(255 * (alphaLeft + alphaCenter + alphaRight) / 3);
+ destRgba[destPixelIndex + 3] = (alphaAvg === 1 ? 0 : alphaAvg);
+ }
+ }
- } );
- }
-
-
- export class HintedTextRenderer {
-
- private textRenderer_VERTSHADER = concatLines(
- ' ',
- ' uniform vec2 u_XyFrac; ',
- ' uniform vec2 u_Anchor; ',
- ' uniform vec2 u_ImageSize; ',
- ' uniform vec2 u_ViewportSize; ',
- ' ',
- ' attribute vec2 a_ImageFrac; ',
- ' ',
- ' varying vec2 v_StCoord; ',
- ' ',
- ' void main( ) { ',
- ' vec2 xy = -1.0 + 2.0*( u_XyFrac + u_ImageSize*( a_ImageFrac - u_Anchor )/u_ViewportSize ); ',
- ' gl_Position = vec4( xy, 0.0, 1.0 ); ',
- ' ',
- ' v_StCoord = vec2( a_ImageFrac.x, 1.0 - a_ImageFrac.y ); ',
- ' } ',
- ' '
- );
-
- private textRenderer_FRAGSHADER = concatLines(
- ' ',
- ' precision mediump float; ',
- ' ',
- ' uniform sampler2D u_Hints; ',
- ' uniform float u_Alpha; ',
- ' ',
- ' varying vec2 v_StCoord; ',
- ' ',
- ' void main( ) { ',
- ' gl_FragColor = u_Alpha * texture2D( u_Hints, v_StCoord ); ',
- ' } ',
- ' '
- );
-
-
- private program : Program;
- private u_XyFrac : Uniform2f;
- private u_Anchor : Uniform2f;
- private u_ImageSize : Uniform2f;
- private u_ViewportSize : Uniform2f;
- private u_Alpha : Uniform1f;
- private u_Hints : UniformSampler2D;
-
- private a_ImageFrac : Attribute;
- private imageFracData : Buffer;
-
- private wViewport : number;
- private hViewport : number;
-
-
- constructor( ) {
- this.program = new Program( this.textRenderer_VERTSHADER, this.textRenderer_FRAGSHADER );
- this.u_XyFrac = new Uniform2f( this.program, 'u_XyFrac' );
- this.u_Anchor = new Uniform2f( this.program, 'u_Anchor' );
- this.u_ImageSize = new Uniform2f( this.program, 'u_ImageSize' );
- this.u_ViewportSize = new Uniform2f( this.program, 'u_ViewportSize' );
- this.u_Alpha = new Uniform1f( this.program, 'u_Alpha' );
- this.u_Hints = new UniformSampler2D( this.program, 'u_Hints' );
-
- this.a_ImageFrac = new Attribute( this.program, 'a_ImageFrac' );
- this.imageFracData = newStaticBuffer( new Float32Array( [ 0.0,0.0, 0.0,1.0, 1.0,0.0, 1.0,1.0 ] ) );
-
- this.wViewport = 0;
- this.hViewport = 0;
- }
-
- begin( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- this.program.use( gl );
- this.u_ViewportSize.setData( gl, viewport.w, viewport.h );
- this.a_ImageFrac.setDataAndEnable( gl, this.imageFracData, 2, GL.FLOAT );
+ g.putImageData(destImage, 0, 0);
+ });
+ },
- this.wViewport = viewport.w;
- this.hViewport = viewport.h;
+ dispose: function (texture: Texture2D) {
+ texture.dispose();
}
- draw( gl : WebGLRenderingContext, hints : Texture2D, xFrac : number, yFrac : number, options? : { xAnchor? : number;
- yAnchor? : number;
- color? : Color } ) {
- var xAnchor = ( hasval( options ) && hasval( options.xAnchor ) ? options.xAnchor : 0.5 );
- var yAnchor = ( hasval( options ) && hasval( options.yAnchor ) ? options.yAnchor : 0.5 );
- var color = ( hasval( options ) && hasval( options.color ) ? options.color : black );
-
- // The hints texture stores subpixel alphas in its R,G,B components. For example, a red hint-pixel indicates
- // that, at that pixel, the final R component should be color.r (R_hint=1, so red is opaque), but the
- // final G and B components should be the G and B from the background pixel (G_hint=0, B_hint=0, so green
- // and blue are transparent).
- //
- // GL does not allow arbitrary blending, but we can use the blending options it does provide to get the effect
- // we want.
- //
- // There are 4 things to keep an eye on:
- //
- // 1. The RGB part of color, which is put into glBlendColor
- // 2. The A part of color, which is sent to the frag-shader (NOT passed to glBlendColor)
- // 3. The subpixel alphas, which are sent to the frag-shader in the hints-texture
- // 4. The glBlendFunc, which is set up in an unusual way
- //
- //
- // With this setup, we get:
- //
- // R_final = ( R_frag )*R_foreground + ( 1 - R_frag )*R_background
- // G_final = ( G_frag )*G_foreground + ( 1 - G_frag )*G_background
- // B_final = ( B_frag )*B_foreground + ( 1 - B_frag )*B_background
- // A_final = ( A_frag )*1 + ( 1 - A_frag )*A_background
- //
- // So R_frag, the output from the frag shader, is a weight that is used to take a weighted average between
- // foreground and background colors.
- //
- // The frag-shader is doing A_foreground*RGBA_hint. Conceptually, R_hint is really a subpixel alpha for the
- // red subpixel, so it is helpful to call it A_redhint (and analogously for G and B):
- //
- // R_frag = A_foreground * A_redhint
- // G_frag = A_foreground * A_greenhint
- // B_frag = A_foreground * A_bluehint
- //
- // So the blended RGB is a weighted average of RGB_foreground and RGB_background, with a weighting factor of
- // ( A_foreground * A_subpixel ), where A_subpixel is either A_redhint, A_greenhint, or A_bluehint. Basically,
- // we have a separate alpha for each subpixel!
- //
- //
- // For the final alpha component, we want:
- //
- // A_final = ( 1 - (1-A_background)*(1-A_frag) )
- //
- // A little algebra shows that to be equivalent to:
- //
- // A_final = ( A_frag )*1 + ( 1 - A_frag )*A_background
- //
- // Which is exactly what we get, as long as we pass an alpha of 1 into glBlendColor.
- //
- //
-
- this.u_XyFrac.setData( gl, nearestPixel( xFrac, this.wViewport, xAnchor, hints.w ), nearestPixel( yFrac, this.hViewport, yAnchor, hints.h ) );
- this.u_Anchor.setData( gl, xAnchor, yAnchor );
- this.u_ImageSize.setData( gl, hints.w, hints.h );
- this.u_Alpha.setData( gl, color.a );
- this.u_Hints.setDataAndBind( gl, 0, hints );
-
- gl.enable( GL.BLEND );
- gl.blendColor( color.r, color.g, color.b, 1 );
- gl.blendFunc( GL.CONSTANT_COLOR, GL.ONE_MINUS_SRC_COLOR );
+ });
+}
+
+
+export class HintedTextRenderer {
+
+ private textRenderer_VERTSHADER = concatLines(
+ ' ',
+ ' uniform vec2 u_XyFrac; ',
+ ' uniform vec2 u_Anchor; ',
+ ' uniform vec2 u_ImageSize; ',
+ ' uniform vec2 u_ViewportSize; ',
+ ' ',
+ ' attribute vec2 a_ImageFrac; ',
+ ' ',
+ ' varying vec2 v_StCoord; ',
+ ' ',
+ ' void main( ) { ',
+ ' vec2 xy = -1.0 + 2.0*( u_XyFrac + u_ImageSize*( a_ImageFrac - u_Anchor )/u_ViewportSize ); ',
+ ' gl_Position = vec4( xy, 0.0, 1.0 ); ',
+ ' ',
+ ' v_StCoord = vec2( a_ImageFrac.x, 1.0 - a_ImageFrac.y ); ',
+ ' } ',
+ ' '
+ );
+
+ private textRenderer_FRAGSHADER = concatLines(
+ ' ',
+ ' precision mediump float; ',
+ ' ',
+ ' uniform sampler2D u_Hints; ',
+ ' uniform float u_Alpha; ',
+ ' ',
+ ' varying vec2 v_StCoord; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_FragColor = u_Alpha * texture2D( u_Hints, v_StCoord ); ',
+ ' } ',
+ ' '
+ );
+
+
+ private program: Program;
+ private u_XyFrac: Uniform2f;
+ private u_Anchor: Uniform2f;
+ private u_ImageSize: Uniform2f;
+ private u_ViewportSize: Uniform2f;
+ private u_Alpha: Uniform1f;
+ private u_Hints: UniformSampler2D;
+
+ private a_ImageFrac: Attribute;
+ private imageFracData: Buffer;
+
+ private wViewport: number;
+ private hViewport: number;
+
+
+ constructor() {
+ this.program = new Program(this.textRenderer_VERTSHADER, this.textRenderer_FRAGSHADER);
+ this.u_XyFrac = new Uniform2f(this.program, 'u_XyFrac');
+ this.u_Anchor = new Uniform2f(this.program, 'u_Anchor');
+ this.u_ImageSize = new Uniform2f(this.program, 'u_ImageSize');
+ this.u_ViewportSize = new Uniform2f(this.program, 'u_ViewportSize');
+ this.u_Alpha = new Uniform1f(this.program, 'u_Alpha');
+ this.u_Hints = new UniformSampler2D(this.program, 'u_Hints');
+
+ this.a_ImageFrac = new Attribute(this.program, 'a_ImageFrac');
+ this.imageFracData = newStaticBuffer(new Float32Array([0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]));
+
+ this.wViewport = 0;
+ this.hViewport = 0;
+ }
- gl.drawArrays( GL.TRIANGLE_STRIP, 0, 4 );
- }
+ begin(gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ this.program.use(gl);
+ this.u_ViewportSize.setData(gl, viewport.w, viewport.h);
+ this.a_ImageFrac.setDataAndEnable(gl, this.imageFracData, 2, GL.FLOAT);
- end( gl : WebGLRenderingContext ) {
- this.a_ImageFrac.disable( gl );
- this.u_Hints.unbind( gl );
- this.program.endUse( gl );
- }
+ this.wViewport = viewport.w;
+ this.hViewport = viewport.h;
}
+ draw(gl: WebGLRenderingContext, hints: Texture2D, xFrac: number, yFrac: number, options?: {
+ xAnchor?: number;
+ yAnchor?: number;
+ color?: Color
+ }) {
+ const xAnchor = (hasval(options) && hasval(options.xAnchor) ? options.xAnchor : 0.5);
+ const yAnchor = (hasval(options) && hasval(options.yAnchor) ? options.yAnchor : 0.5);
+ const color = (hasval(options) && hasval(options.color) ? options.color : black);
+
+ // The hints texture stores subpixel alphas in its R,G,B components. For example, a red hint-pixel indicates
+ // that, at that pixel, the final R component should be color.r (R_hint=1, so red is opaque), but the
+ // final G and B components should be the G and B from the background pixel (G_hint=0, B_hint=0, so green
+ // and blue are transparent).
+ //
+ // GL does not allow arbitrary blending, but we can use the blending options it does provide to get the effect
+ // we want.
+ //
+ // There are 4 things to keep an eye on:
+ //
+ // 1. The RGB part of color, which is put into glBlendColor
+ // 2. The A part of color, which is sent to the frag-shader (NOT passed to glBlendColor)
+ // 3. The subpixel alphas, which are sent to the frag-shader in the hints-texture
+ // 4. The glBlendFunc, which is set up in an unusual way
+ //
+ //
+ // With this setup, we get:
+ //
+ // R_final = ( R_frag )*R_foreground + ( 1 - R_frag )*R_background
+ // G_final = ( G_frag )*G_foreground + ( 1 - G_frag )*G_background
+ // B_final = ( B_frag )*B_foreground + ( 1 - B_frag )*B_background
+ // A_final = ( A_frag )*1 + ( 1 - A_frag )*A_background
+ //
+ // So R_frag, the output from the frag shader, is a weight that is used to take a weighted average between
+ // foreground and background colors.
+ //
+ // The frag-shader is doing A_foreground*RGBA_hint. Conceptually, R_hint is really a subpixel alpha for the
+ // red subpixel, so it is helpful to call it A_redhint (and analogously for G and B):
+ //
+ // R_frag = A_foreground * A_redhint
+ // G_frag = A_foreground * A_greenhint
+ // B_frag = A_foreground * A_bluehint
+ //
+ // So the blended RGB is a weighted average of RGB_foreground and RGB_background, with a weighting factor of
+ // ( A_foreground * A_subpixel ), where A_subpixel is either A_redhint, A_greenhint, or A_bluehint. Basically,
+ // we have a separate alpha for each subpixel!
+ //
+ //
+ // For the final alpha component, we want:
+ //
+ // A_final = ( 1 - (1-A_background)*(1-A_frag) )
+ //
+ // A little algebra shows that to be equivalent to:
+ //
+ // A_final = ( A_frag )*1 + ( 1 - A_frag )*A_background
+ //
+ // Which is exactly what we get, as long as we pass an alpha of 1 into glBlendColor.
+ //
+ //
+
+ this.u_XyFrac.setData(gl, nearestPixel(xFrac, this.wViewport, xAnchor, hints.w), nearestPixel(yFrac, this.hViewport, yAnchor, hints.h));
+ this.u_Anchor.setData(gl, xAnchor, yAnchor);
+ this.u_ImageSize.setData(gl, hints.w, hints.h);
+ this.u_Alpha.setData(gl, color.a);
+ this.u_Hints.setDataAndBind(gl, 0, hints);
+
+ gl.enable(GL.BLEND);
+ gl.blendColor(color.r, color.g, color.b, 1);
+ gl.blendFunc(GL.CONSTANT_COLOR, GL.ONE_MINUS_SRC_COLOR);
+
+ gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
+ }
-}
\ No newline at end of file
+ end(gl: WebGLRenderingContext) {
+ this.a_ImageFrac.disable(gl);
+ this.u_Hints.unbind(gl);
+ this.program.endUse(gl);
+ }
+}
diff --git a/src/webglimpse/texture.ts b/src/webglimpse/texture.ts
index 41abcee..1573d57 100644
--- a/src/webglimpse/texture.ts
+++ b/src/webglimpse/texture.ts
@@ -27,273 +27,275 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { StringMap, GL, concatLines, getObjectId, hasval } from './util/util';
+import { Program, Uniform2f, Uniform1f, UniformSampler2D, Attribute } from './shader';
+import { Buffer, newStaticBuffer } from './buffer';
+import { BoundsUnmodifiable } from './bounds';
+import { nearestPixel } from './misc';
+
+class TextureEntry {
+ gl: WebGLRenderingContext;
+ target: number;
+ texture: WebGLTexture;
+ textureUnit: number;
+
+ constructor(gl: WebGLRenderingContext, target: number, texture: WebGLTexture) {
+ this.gl = gl;
+ this.target = target;
+ this.texture = texture;
+ this.textureUnit = -1;
+ }
+}
- class TextureEntry {
- gl : WebGLRenderingContext;
- target : number;
- texture : WebGLTexture;
- textureUnit : number;
+export interface TextureHelper {
+ target(gl: WebGLRenderingContext): number;
+ init(gl: WebGLRenderingContext, target: number): void;
+}
- constructor( gl : WebGLRenderingContext, target : number, texture : WebGLTexture ) {
- this.gl = gl;
- this.target = target;
- this.texture = texture;
- this.textureUnit = -1;
- }
- }
+export class Texture {
+ private helper: TextureHelper;
+ private textures: StringMap;
- export interface TextureHelper {
- target( gl : WebGLRenderingContext ) : number;
- init( gl : WebGLRenderingContext, target : number );
+ constructor(helper: TextureHelper) {
+ this.helper = helper;
+ this.textures = {};
}
-
- export class Texture {
- private helper : TextureHelper;
- private textures : StringMap;
-
- constructor( helper : TextureHelper ) {
- this.helper = helper;
- this.textures = { };
+ bind(gl: WebGLRenderingContext, textureUnit: number) {
+ const glId = getObjectId(gl);
+ if (hasval(this.textures[glId])) {
+ const en = this.textures[glId];
+ gl.activeTexture(GL.TEXTURE0 + textureUnit);
+ gl.bindTexture(en.target, en.texture);
+ en.textureUnit = textureUnit;
}
-
- bind( gl : WebGLRenderingContext, textureUnit : number ) {
- var glId = getObjectId( gl );
- if ( hasval( this.textures[ glId ] ) ) {
- var en = this.textures[ glId ];
- gl.activeTexture( GL.TEXTURE0 + textureUnit );
- gl.bindTexture( en.target, en.texture );
- en.textureUnit = textureUnit;
- }
- else {
- var target = this.helper.target( gl );
- var texture = gl.createTexture( );
- if ( !hasval( texture ) ) throw new Error( 'Failed to create texture' );
- this.textures[ glId ] = new TextureEntry( gl, target, texture );
-
- var en = this.textures[ glId ];
- gl.activeTexture( GL.TEXTURE0 + textureUnit );
- gl.bindTexture( en.target, en.texture );
- en.textureUnit = textureUnit;
-
- this.helper.init( gl, target );
+ else {
+ const target = this.helper.target(gl);
+ const texture = gl.createTexture();
+ if (!hasval(texture)) {
+ throw new Error('Failed to create texture');
}
+ this.textures[glId] = new TextureEntry(gl, target, texture);
+
+ const en = this.textures[glId];
+ gl.activeTexture(GL.TEXTURE0 + textureUnit);
+ gl.bindTexture(en.target, en.texture);
+ en.textureUnit = textureUnit;
+
+ this.helper.init(gl, target);
}
+ }
- unbind( gl : WebGLRenderingContext ) {
- var glId = getObjectId( gl );
- if ( hasval( this.textures[ glId ] ) ) {
- var en = this.textures[ glId ];
- gl.activeTexture( GL.TEXTURE0 + en.textureUnit );
- gl.bindTexture( en.target, null );
- en.textureUnit = -1;
- }
+ unbind(gl: WebGLRenderingContext) {
+ const glId = getObjectId(gl);
+ if (hasval(this.textures[glId])) {
+ const en = this.textures[glId];
+ gl.activeTexture(GL.TEXTURE0 + en.textureUnit);
+ gl.bindTexture(en.target, null);
+ en.textureUnit = -1;
}
+ }
- dispose( ) {
- // XXX: Not sure this actually works ... may have to make each gl current or something
- for ( var glid in this.textures ) {
- if ( this.textures.hasOwnProperty( glid ) ) {
- var en = this.textures[ glid ];
- en.gl.deleteTexture( en.texture );
- }
+ dispose() {
+ // XXX: Not sure this actually works ... may have to make each gl current or something
+ for (const glid in this.textures) {
+ if (this.textures.hasOwnProperty(glid)) {
+ const en = this.textures[glid];
+ en.gl.deleteTexture(en.texture);
}
- this.textures = { };
}
+ this.textures = {};
}
+}
- export interface ImageDrawer {
- ( context : CanvasRenderingContext2D );
- }
+export type ImageDrawer = (context: CanvasRenderingContext2D) => void;
- export class FloatDataTexture2D extends Texture {
- private _w : number;
- private _h : number;
-
- get w( ) : number { return this._w; }
- get h( ) : number { return this._h; }
-
- constructor( w : number, h : number, array : Float32Array ) {
- this._w = w;
- this._h = h;
-
- super( {
- target: function( gl : WebGLRenderingContext ) : number {
- return GL.TEXTURE_2D;
- },
- init: function( gl : WebGLRenderingContext, target : number ) {
-
- if ( !gl.getExtension( 'OES_texture_float' ) ) {
- throw new Error( 'OES_texture_float extension is required' );
- }
-
- gl.texParameteri( target, GL.TEXTURE_MAG_FILTER, GL.NEAREST );
- gl.texParameteri( target, GL.TEXTURE_MIN_FILTER, GL.NEAREST );
- gl.texParameteri( target, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE );
- gl.texParameteri( target, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE );
-
- // GL.LUMINANCE isn't supported with GL.FLOAT
- gl.texImage2D( target, 0, GL.RGBA, w, h, 0, GL.RGBA, GL.FLOAT, array );
- }
- } );
- }
- }
+export class FloatDataTexture2D extends Texture {
+ private _w: number;
+ private _h: number;
- export class Texture2D extends Texture {
- private _w : number;
- private _h : number;
-
- get w( ) : number { return this._w; }
- get h( ) : number { return this._h; }
-
- constructor( w : number, h : number, minFilter : number, magFilter : number, draw : ImageDrawer ) {
- this._w = w;
- this._h = h;
-
- var canvas = document.createElement( 'canvas' );
- canvas.width = w;
- canvas.height = h;
- draw( canvas.getContext( '2d' ) );
-
- super( {
- target: function( gl : WebGLRenderingContext ) : number {
- return GL.TEXTURE_2D;
- },
- init: function( gl : WebGLRenderingContext, target : number ) {
- gl.texParameteri( target, GL.TEXTURE_MAG_FILTER, magFilter );
- gl.texParameteri( target, GL.TEXTURE_MIN_FILTER, minFilter );
- gl.texParameteri( target, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE );
- gl.texParameteri( target, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE );
- gl.texImage2D( target, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, canvas );
+ get w(): number { return this._w; }
+ get h(): number { return this._h; }
+
+ constructor(w: number, h: number, array: Float32Array) {
+ super({
+ target: function (gl: WebGLRenderingContext): number {
+ return GL.TEXTURE_2D;
+ },
+ init: function (gl: WebGLRenderingContext, target: number) {
+
+ if (!gl.getExtension('OES_texture_float')) {
+ throw new Error('OES_texture_float extension is required');
}
- } );
- }
- }
+ gl.texParameteri(target, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
+ gl.texParameteri(target, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
+ gl.texParameteri(target, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
+ gl.texParameteri(target, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
- export interface TextureDrawOptions {
- xAnchor? : number;
- yAnchor? : number;
- rotation_CCWRAD? : number;
- width? : number;
- height? : number;
- }
+ // GL.LUMINANCE isn't supported with GL.FLOAT
+ gl.texImage2D(target, 0, GL.RGBA, w, h, 0, GL.RGBA, GL.FLOAT, array);
+ }
+ });
+ this._w = w;
+ this._h = h;
+ }
+}
+
+export class Texture2D extends Texture {
+ private _w: number;
+ private _h: number;
+
+ get w(): number { return this._w; }
+ get h(): number { return this._h; }
+
+ constructor(w: number, h: number, minFilter: number, magFilter: number, draw: ImageDrawer) {
+ const canvas = document.createElement('canvas');
+ canvas.width = w;
+ canvas.height = h;
+ draw(canvas.getContext('2d'));
+
+ super({
+ target: function (gl: WebGLRenderingContext): number {
+ return GL.TEXTURE_2D;
+ },
+ init: function (gl: WebGLRenderingContext, target: number) {
+ gl.texParameteri(target, GL.TEXTURE_MAG_FILTER, magFilter);
+ gl.texParameteri(target, GL.TEXTURE_MIN_FILTER, minFilter);
+ gl.texParameteri(target, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
+ gl.texParameteri(target, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
+ gl.texImage2D(target, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, canvas);
+ }
+ });
- export class TextureRenderer {
-
- private textureRenderer_VERTSHADER = concatLines(
- ' ',
- ' uniform vec2 u_XyFrac; ',
- ' uniform vec2 u_Anchor; ',
- ' uniform float u_Rotation_CCWRAD; ',
- ' uniform vec2 u_ImageSize; ',
- ' uniform vec2 u_ViewportSize; ',
- ' ',
- ' attribute vec2 a_ImageFrac; ',
- ' ',
- ' varying vec2 v_StCoord; ',
- ' ',
- ' void main( ) { ',
- ' float cosRot = cos( u_Rotation_CCWRAD ); ',
- ' float sinRot = sin( u_Rotation_CCWRAD ); ',
- ' ',
- ' // Column major ',
- ' mat2 rotation = mat2( cosRot, sinRot, ',
- ' -sinRot, cosRot ); ',
- ' ',
- ' vec2 xy = -1.0 + 2.0*( u_XyFrac + rotation*( u_ImageSize*( a_ImageFrac - u_Anchor ) ) / u_ViewportSize ); ',
- ' gl_Position = vec4( xy, 0.0, 1.0 ); ',
- ' ',
- ' v_StCoord = vec2( a_ImageFrac.x, 1.0 - a_ImageFrac.y ); ',
- ' } ',
- ' '
- );
-
- private textureRenderer_FRAGSHADER = concatLines(
- ' ',
- ' precision mediump float; ',
- ' ',
- ' uniform sampler2D u_Sampler; ',
- ' ',
- ' varying vec2 v_StCoord; ',
- ' ',
- ' void main( ) { ',
- ' gl_FragColor = texture2D( u_Sampler, v_StCoord ); ',
- ' } ',
- ' '
- );
-
-
- private program : Program;
- private u_XyFrac : Uniform2f;
- private u_Anchor : Uniform2f;
- private u_Rotation_CCWRAD : Uniform1f;
- private u_ImageSize : Uniform2f;
- private u_ViewportSize : Uniform2f;
- private u_Sampler : UniformSampler2D;
-
- private a_ImageFrac : Attribute;
-
- private imageFracData : Buffer;
-
- private wViewport : number;
- private hViewport : number;
-
-
- constructor( ) {
- this.program = new Program( this.textureRenderer_VERTSHADER, this.textureRenderer_FRAGSHADER );
- this.u_XyFrac = new Uniform2f( this.program, 'u_XyFrac' );
- this.u_Anchor = new Uniform2f( this.program, 'u_Anchor' );
- this.u_Rotation_CCWRAD = new Uniform1f( this.program, 'u_Rotation_CCWRAD' );
- this.u_ImageSize = new Uniform2f( this.program, 'u_ImageSize' );
- this.u_ViewportSize = new Uniform2f( this.program, 'u_ViewportSize' );
- this.u_Sampler = new UniformSampler2D( this.program, 'u_Sampler' );
-
- this.a_ImageFrac = new Attribute( this.program, 'a_ImageFrac' );
- this.imageFracData = newStaticBuffer( new Float32Array( [ 0.0,0.0, 0.0,1.0, 1.0,0.0, 1.0,1.0 ] ) );
-
- this.wViewport = 0;
- this.hViewport = 0;
- }
+ this._w = w;
+ this._h = h;
+ }
+}
+
+
+export interface TextureDrawOptions {
+ xAnchor?: number;
+ yAnchor?: number;
+ rotation_CCWRAD?: number;
+ width?: number;
+ height?: number;
+}
+
+
+export class TextureRenderer {
+
+ private textureRenderer_VERTSHADER = concatLines(
+ ' ',
+ ' uniform vec2 u_XyFrac; ',
+ ' uniform vec2 u_Anchor; ',
+ ' uniform float u_Rotation_CCWRAD; ',
+ ' uniform vec2 u_ImageSize; ',
+ ' uniform vec2 u_ViewportSize; ',
+ ' ',
+ ' attribute vec2 a_ImageFrac; ',
+ ' ',
+ ' varying vec2 v_StCoord; ',
+ ' ',
+ ' void main( ) { ',
+ ' float cosRot = cos( u_Rotation_CCWRAD ); ',
+ ' float sinRot = sin( u_Rotation_CCWRAD ); ',
+ ' ',
+ ' // Column major ',
+ ' mat2 rotation = mat2( cosRot, sinRot, ',
+ ' -sinRot, cosRot ); ',
+ ' ',
+ ' vec2 xy = -1.0 + 2.0*( u_XyFrac + rotation*( u_ImageSize*( a_ImageFrac - u_Anchor ) ) / u_ViewportSize ); ',
+ ' gl_Position = vec4( xy, 0.0, 1.0 ); ',
+ ' ',
+ ' v_StCoord = vec2( a_ImageFrac.x, 1.0 - a_ImageFrac.y ); ',
+ ' } ',
+ ' '
+ );
+
+ private textureRenderer_FRAGSHADER = concatLines(
+ ' ',
+ ' precision mediump float; ',
+ ' ',
+ ' uniform sampler2D u_Sampler; ',
+ ' ',
+ ' varying vec2 v_StCoord; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_FragColor = texture2D( u_Sampler, v_StCoord ); ',
+ ' } ',
+ ' '
+ );
+
+
+ private program: Program;
+ private u_XyFrac: Uniform2f;
+ private u_Anchor: Uniform2f;
+ private u_Rotation_CCWRAD: Uniform1f;
+ private u_ImageSize: Uniform2f;
+ private u_ViewportSize: Uniform2f;
+ private u_Sampler: UniformSampler2D;
+
+ private a_ImageFrac: Attribute;
+
+ private imageFracData: Buffer;
+
+ private wViewport: number;
+ private hViewport: number;
+
+
+ constructor() {
+ this.program = new Program(this.textureRenderer_VERTSHADER, this.textureRenderer_FRAGSHADER);
+ this.u_XyFrac = new Uniform2f(this.program, 'u_XyFrac');
+ this.u_Anchor = new Uniform2f(this.program, 'u_Anchor');
+ this.u_Rotation_CCWRAD = new Uniform1f(this.program, 'u_Rotation_CCWRAD');
+ this.u_ImageSize = new Uniform2f(this.program, 'u_ImageSize');
+ this.u_ViewportSize = new Uniform2f(this.program, 'u_ViewportSize');
+ this.u_Sampler = new UniformSampler2D(this.program, 'u_Sampler');
+
+ this.a_ImageFrac = new Attribute(this.program, 'a_ImageFrac');
+ this.imageFracData = newStaticBuffer(new Float32Array([0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]));
+
+ this.wViewport = 0;
+ this.hViewport = 0;
+ }
- begin( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
+ begin(gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
- this.program.use( gl );
- this.u_ViewportSize.setData( gl, viewport.w, viewport.h );
- this.a_ImageFrac.setDataAndEnable( gl, this.imageFracData, 2, GL.FLOAT );
+ this.program.use(gl);
+ this.u_ViewportSize.setData(gl, viewport.w, viewport.h);
+ this.a_ImageFrac.setDataAndEnable(gl, this.imageFracData, 2, GL.FLOAT);
- this.wViewport = viewport.w;
- this.hViewport = viewport.h;
- }
+ this.wViewport = viewport.w;
+ this.hViewport = viewport.h;
+ }
- draw( gl : WebGLRenderingContext, texture : Texture2D, xFrac : number, yFrac : number, options? : TextureDrawOptions ) {
- var xAnchor = ( hasval( options ) && hasval( options.xAnchor ) ? options.xAnchor : 0.5 );
- var yAnchor = ( hasval( options ) && hasval( options.yAnchor ) ? options.yAnchor : 0.5 );
- var rotation_CCWRAD = ( hasval( options ) && hasval( options.rotation_CCWRAD ) ? options.rotation_CCWRAD : 0 );
- var width = ( hasval( options ) && hasval( options.width ) ? options.width : texture.w );
- var height = ( hasval( options ) && hasval( options.height ) ? options.height : texture.h );
-
- this.u_XyFrac.setData( gl, nearestPixel( xFrac, this.wViewport, xAnchor, texture.w ), nearestPixel( yFrac, this.hViewport, yAnchor, texture.h ) );
- this.u_Anchor.setData( gl, xAnchor, yAnchor );
- this.u_Rotation_CCWRAD.setData( gl, rotation_CCWRAD );
- this.u_ImageSize.setData( gl, width, height );
- this.u_Sampler.setDataAndBind( gl, 0, texture );
- gl.drawArrays( GL.TRIANGLE_STRIP, 0, 4 );
- }
+ draw(gl: WebGLRenderingContext, texture: Texture2D, xFrac: number, yFrac: number, options?: TextureDrawOptions) {
+ const xAnchor = (hasval(options) && hasval(options.xAnchor) ? options.xAnchor : 0.5);
+ const yAnchor = (hasval(options) && hasval(options.yAnchor) ? options.yAnchor : 0.5);
+ const rotation_CCWRAD = (hasval(options) && hasval(options.rotation_CCWRAD) ? options.rotation_CCWRAD : 0);
+ const width = (hasval(options) && hasval(options.width) ? options.width : texture.w);
+ const height = (hasval(options) && hasval(options.height) ? options.height : texture.h);
+
+ this.u_XyFrac.setData(gl, nearestPixel(xFrac, this.wViewport, xAnchor, texture.w), nearestPixel(yFrac, this.hViewport, yAnchor, texture.h));
+ this.u_Anchor.setData(gl, xAnchor, yAnchor);
+ this.u_Rotation_CCWRAD.setData(gl, rotation_CCWRAD);
+ this.u_ImageSize.setData(gl, width, height);
+ this.u_Sampler.setDataAndBind(gl, 0, texture);
+ gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
+ }
- end( gl : WebGLRenderingContext ) {
- this.a_ImageFrac.disable( gl );
- this.u_Sampler.unbind( gl );
- this.program.endUse( gl );
- }
+ end(gl: WebGLRenderingContext) {
+ this.a_ImageFrac.disable(gl);
+ this.u_Sampler.unbind(gl);
+ this.program.endUse(gl);
}
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/time.ts b/src/webglimpse/time.ts
index 1d916f0..1e781c3 100644
--- a/src/webglimpse/time.ts
+++ b/src/webglimpse/time.ts
@@ -27,40 +27,39 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
- export function secondsToMillis( value_SECONDS : number ) : number {
- return value_SECONDS * 1000;
- }
- export function millisToSeconds( value_MILLIS : number ) : number {
- return value_MILLIS / 1000;
- }
+export function secondsToMillis(value_SECONDS: number): number {
+ return value_SECONDS * 1000;
+}
- export function minutesToMillis( value_MINUTES : number ) : number {
- return value_MINUTES * 60000;
- }
+export function millisToSeconds(value_MILLIS: number): number {
+ return value_MILLIS / 1000;
+}
- export function millisToMinutes( value_MILLIS : number ) : number {
- return value_MILLIS / 60000;
- }
+export function minutesToMillis(value_MINUTES: number): number {
+ return value_MINUTES * 60000;
+}
- export function hoursToMillis( value_HOURS : number ) : number {
- return value_HOURS * 3600000;
- }
+export function millisToMinutes(value_MILLIS: number): number {
+ return value_MILLIS / 60000;
+}
- export function millisToHours( value_MILLIS : number ) : number {
- return value_MILLIS / 3600000;
- }
+export function hoursToMillis(value_HOURS: number): number {
+ return value_HOURS * 3600000;
+}
- export function daysToMillis( value_DAYS : number ) : number {
- return value_DAYS * 86400000;
- }
+export function millisToHours(value_MILLIS: number): number {
+ return value_MILLIS / 3600000;
+}
- export function millisToDays( value_MILLIS : number ) : number {
- return value_MILLIS / 86400000;
- }
+export function daysToMillis(value_DAYS: number): number {
+ return value_DAYS * 86400000;
+}
+
+export function millisToDays(value_MILLIS: number): number {
+ return value_MILLIS / 86400000;
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/timeline/time_axis.ts b/src/webglimpse/timeline/time_axis.ts
index 85f81c5..d1bc211 100644
--- a/src/webglimpse/timeline/time_axis.ts
+++ b/src/webglimpse/timeline/time_axis.ts
@@ -27,63 +27,71 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Axis1D } from '../plot/axis';
- export class TimeAxis1D extends Axis1D {
-
- private _epoch_PMILLIS : number;
-
- constructor( tMin_PMILLIS : number, tMax_PMILLIS : number ) {
- this._epoch_PMILLIS = 0.5*( tMin_PMILLIS + tMax_PMILLIS );
- super( tMin_PMILLIS - this._epoch_PMILLIS, tMax_PMILLIS - this._epoch_PMILLIS );
- }
+export class TimeAxis1D extends Axis1D {
- get tMin_PMILLIS( ) : number {
- return ( this._epoch_PMILLIS + this.vMin );
- }
+ private _epoch_PMILLIS: number;
- get tMax_PMILLIS( ) : number {
- return ( this._epoch_PMILLIS + this.vMax );
- }
+ constructor(tMin_PMILLIS: number, tMax_PMILLIS: number, tMinLimit_PMILLIS = 0, tMaxLimit_PMILLIS = 253402329599000) {
+ const epoch_PMILLIS = 0.5 * (tMin_PMILLIS + tMax_PMILLIS);
+ super(tMin_PMILLIS - epoch_PMILLIS, tMax_PMILLIS - epoch_PMILLIS, tMinLimit_PMILLIS - epoch_PMILLIS, tMaxLimit_PMILLIS - epoch_PMILLIS);
+ this._epoch_PMILLIS = epoch_PMILLIS;
+ }
- set tMin_PMILLIS( tMin_PMILLIS : number ) {
- this.vMin = ( tMin_PMILLIS - this._epoch_PMILLIS );
- }
+ get tMin_PMILLIS(): number {
+ return (this._epoch_PMILLIS + this.vMin);
+ }
- set tMax_PMILLIS( tMax_PMILLIS : number ) {
- this.vMax = ( tMax_PMILLIS - this._epoch_PMILLIS );
- }
+ get tMax_PMILLIS(): number {
+ return (this._epoch_PMILLIS + this.vMax);
+ }
- setTRange_PMILLIS( tMin_PMILLIS : number, tMax_PMILLIS : number ) {
- this.setVRange( tMin_PMILLIS - this._epoch_PMILLIS, tMax_PMILLIS - this._epoch_PMILLIS );
- }
+ get tMinLimit_PMILLIS(): number {
+ return (this._epoch_PMILLIS + this.vMinLimit);
+ }
- get tSize_MILLIS( ) : number {
- return this.vSize;
- }
+ get tMaxLimit_PMILLIS(): number {
+ return (this._epoch_PMILLIS + this.vMaxLimit);
+ }
- vAtTime( t_PMILLIS : number ) : number {
- return ( t_PMILLIS - this._epoch_PMILLIS );
- }
+ set tMin_PMILLIS(tMin_PMILLIS: number) {
+ this.vMin = (tMin_PMILLIS - this._epoch_PMILLIS);
+ }
- tAtFrac_PMILLIS( tFrac : number ) : number {
- return ( this._epoch_PMILLIS + this.vAtFrac( tFrac ) );
- }
+ set tMax_PMILLIS(tMax_PMILLIS: number) {
+ this.vMax = (tMax_PMILLIS - this._epoch_PMILLIS);
+ }
- tFrac( t_PMILLIS : number ) : number {
- return this.vFrac( t_PMILLIS - this._epoch_PMILLIS );
- }
+ setTRange_PMILLIS(tMin_PMILLIS: number, tMax_PMILLIS: number) {
+ this.setVRange(tMin_PMILLIS - this._epoch_PMILLIS, tMax_PMILLIS - this._epoch_PMILLIS);
+ }
- tPan( tAmount_MILLIS : number ) {
- this.pan( tAmount_MILLIS );
- }
+ get tSize_MILLIS(): number {
+ return this.vSize;
+ }
- tZoom( factor : number, tAnchor_PMILLIS : number ) {
- this.zoom( factor, tAnchor_PMILLIS - this._epoch_PMILLIS );
- }
+ vAtTime(t_PMILLIS: number): number {
+ return (t_PMILLIS - this._epoch_PMILLIS);
+ }
+
+ tAtFrac_PMILLIS(tFrac: number): number {
+ return (this._epoch_PMILLIS + this.vAtFrac(tFrac));
+ }
+
+ tFrac(t_PMILLIS: number): number {
+ return this.vFrac(t_PMILLIS - this._epoch_PMILLIS);
+ }
+
+ tPan(tAmount_MILLIS: number) {
+ this.pan(tAmount_MILLIS);
+ }
+ tZoom(factor: number, tAnchor_PMILLIS: number) {
+ this.zoom(factor, tAnchor_PMILLIS - this._epoch_PMILLIS);
}
+}
+
-}
\ No newline at end of file
diff --git a/src/webglimpse/timeline/time_axis_painter.ts b/src/webglimpse/timeline/time_axis_painter.ts
index c23e90c..e94f936 100644
--- a/src/webglimpse/timeline/time_axis_painter.ts
+++ b/src/webglimpse/timeline/time_axis_painter.ts
@@ -27,812 +27,863 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
-
- // XXX: Much of this is duplicated from edge_axis_painter
-
-
- export interface TimeAxisPainterOptions {
- tickSpacing? : number;
- font? : string;
- textColor? : Color;
- tickColor? : Color;
- tickSize? : number;
- labelAlign? : number;
- referenceDate? : string;
- // if true, relative date labels in the future are positive (Day 1)
- // if false, relative date labels in the future are negative (Day -1)
- isFuturePositive? : boolean;
- }
-
-
- export function newTimeAxisPainter( timeAxis : TimeAxis1D, labelSide : Side, displayTimeZone : string, tickTimeZone : string, options? : TimeAxisPainterOptions ) : Painter {
- var tickSpacing = ( hasval( options ) && hasval( options.tickSpacing ) ? options.tickSpacing : 60 );
- var font = ( hasval( options ) && hasval( options.font ) ? options.font : '11px verdana,sans-serif' );
- var textColor = ( hasval( options ) && hasval( options.textColor ) ? options.textColor : black );
- var tickColor = ( hasval( options ) && hasval( options.tickColor ) ? options.tickColor : black );
- var tickSize = ( hasval( options ) && hasval( options.tickSize ) ? options.tickSize : 6 );
- var labelAlign = ( hasval( options ) && hasval( options.labelAlign ) ? options.labelAlign : 0.5 );
- var referenceDate_PMILLIS = ( hasval( options ) && hasval( options.referenceDate ) ? parseTime_PMILLIS( options.referenceDate ) : undefined );
- var isFuturePositive = ( hasval( options ) && hasval( options.isFuturePositive ) ? options.isFuturePositive : true );
-
- var marksProgram = new Program( edgeMarks_VERTSHADER( labelSide ), solid_FRAGSHADER );
- var marksProgram_u_VMin = new Uniform1f( marksProgram, 'u_VMin' );
- var marksProgram_u_VSize = new Uniform1f( marksProgram, 'u_VSize' );
- var marksProgram_u_ViewportSize = new Uniform2f( marksProgram, 'u_ViewportSize' );
- var marksProgram_u_MarkSize = new Uniform1f( marksProgram, 'u_MarkSize' );
- var marksProgram_u_Color = new UniformColor( marksProgram, 'u_Color' );
- var marksProgram_a_VCoord = new Attribute( marksProgram, 'a_VCoord' );
-
- var markCoords = new Float32Array( 0 );
- var markCoordsBuffer = newDynamicBuffer( );
-
- var textTextures = > newTextTextureCache( font, textColor );
- var textureRenderer = new TextureRenderer( );
- var hTickLabels = textTextures.value( '-0123456789:.' ).h;
- var isVerticalAxis = ( labelSide === Side.LEFT || labelSide === Side.RIGHT );
-
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
-
- var sizePixels = isVerticalAxis ? viewport.h : viewport.w;
- if ( sizePixels === 0 ) return;
-
- var tickTimes_PMILLIS = getTickTimes_PMILLIS( timeAxis, sizePixels, tickSpacing, tickTimeZone, referenceDate_PMILLIS );
- var tickInterval_MILLIS = getTickInterval_MILLIS( tickTimes_PMILLIS );
- var tickCount = tickTimes_PMILLIS.length;
-
-
- // Tick marks
- //
-
- marksProgram.use( gl );
- marksProgram_u_VMin.setData( gl, timeAxis.vMin );
- marksProgram_u_VSize.setData( gl, timeAxis.vSize );
- marksProgram_u_ViewportSize.setData( gl, viewport.w, viewport.h );
- marksProgram_u_MarkSize.setData( gl, tickSize );
- marksProgram_u_Color.setData( gl, tickColor );
-
- markCoords = ensureCapacityFloat32( markCoords, 4*tickCount );
- for ( var n = 0; n < tickCount; n++ ) {
- var v = timeAxis.vAtTime( tickTimes_PMILLIS[ n ] );
- markCoords[ ( 4*n + 0 ) ] = v;
- markCoords[ ( 4*n + 1 ) ] = 0;
- markCoords[ ( 4*n + 2 ) ] = v;
- markCoords[ ( 4*n + 3 ) ] = 1;
- }
- markCoordsBuffer.setData( markCoords.subarray( 0, 4*tickCount ) );
- marksProgram_a_VCoord.setDataAndEnable( gl, markCoordsBuffer, 2, GL.FLOAT );
+import { Color, black } from '../color';
+import { TimeAxis1D } from './time_axis';
+import { Side, solid_FRAGSHADER } from '../misc';
+import { Painter } from '../core';
+import { Program, Uniform1f, Uniform2f, UniformColor, Attribute } from '../shader';
+import { edgeMarks_VERTSHADER } from '../plot/edge_axis_painter';
+import { newDynamicBuffer } from '../buffer';
+import { Cache } from '../util/cache';
+import { TextTexture2D, newTextTextureCache } from '../text';
+import { TextureRenderer, TextureDrawOptions } from '../texture';
+import { BoundsUnmodifiable } from '../bounds';
+import { ensureCapacityFloat32, GL, hasval, parseTime_PMILLIS, clamp } from '../util/util';
+import { minutesToMillis, millisToDays, hoursToMillis, daysToMillis, millisToHours, secondsToMillis } from '../time';
+import { Axis1D, getTickInterval, getTickCount, getTickPositions } from '../plot/axis';
+import moment, { Moment } from 'moment';
+
+// XXX: Much of this is duplicated from edge_axis_painter
+
+export class FormatOptions {
+ tickFormat: string;
+ prefixFormat: string;
+ constructor(tick: string, prefix: string) {
+ this.tickFormat = tick;
+ this.prefixFormat = prefix;
+ }
+}
+
+export interface TimeAxisFormatOptions {
+ hour?: FormatOptions;
+ day?: FormatOptions;
+ month?: FormatOptions;
+ year?: FormatOptions;
+}
+
+export interface TimeAxisPainterOptions {
+ tickSpacing?: number;
+ font?: string;
+ textColor?: Color;
+ tickColor?: Color;
+ tickSize?: number;
+ labelAlign?: number;
+ referenceDate?: string;
+ timeAxisFormat?: TimeAxisFormatOptions;
+ // if true, relative date labels in the future are positive (Day 1)
+ // if false, relative date labels in the future are negative (Day -1)
+ isFuturePositive?: boolean;
+}
+
+
+export function newTimeAxisPainter(timeAxis: TimeAxis1D, labelSide: Side, displayTimeZone: string, tickTimeZone: string, options?: TimeAxisPainterOptions): Painter {
+ const tickSpacing = (hasval(options) && hasval(options.tickSpacing) ? options.tickSpacing : 60);
+ const font = (hasval(options) && hasval(options.font) ? options.font : '11px verdana,sans-serif');
+ const textColor = (hasval(options) && hasval(options.textColor) ? options.textColor : black);
+ const tickColor = (hasval(options) && hasval(options.tickColor) ? options.tickColor : black);
+ const tickSize = (hasval(options) && hasval(options.tickSize) ? options.tickSize : 6);
+ const labelAlign = (hasval(options) && hasval(options.labelAlign) ? options.labelAlign : 0.5);
+ const referenceDate_PMILLIS = (hasval(options) && hasval(options.referenceDate) ? parseTime_PMILLIS(options.referenceDate) : undefined);
+ const isFuturePositive = (hasval(options) && hasval(options.isFuturePositive) ? options.isFuturePositive : true);
+ const timeAxisFormat = (hasval(options) && hasval(options.timeAxisFormat) ? options.timeAxisFormat : undefined);
+
+ const marksProgram = new Program(edgeMarks_VERTSHADER(labelSide), solid_FRAGSHADER);
+ const marksProgram_u_VMin = new Uniform1f(marksProgram, 'u_VMin');
+ const marksProgram_u_VSize = new Uniform1f(marksProgram, 'u_VSize');
+ const marksProgram_u_ViewportSize = new Uniform2f(marksProgram, 'u_ViewportSize');
+ const marksProgram_u_MarkSize = new Uniform1f(marksProgram, 'u_MarkSize');
+ const marksProgram_u_Color = new UniformColor(marksProgram, 'u_Color');
+ const marksProgram_a_VCoord = new Attribute(marksProgram, 'a_VCoord');
+
+ let markCoords = new Float32Array(0);
+ const markCoordsBuffer = newDynamicBuffer();
- // IE does not support lineWidths other than 1, so make sure all browsers use lineWidth of 1
- gl.lineWidth( 1 );
- gl.drawArrays( GL.LINES, 0, 2*tickCount );
+ const textTextures = >newTextTextureCache(font, textColor);
+ const textureRenderer = new TextureRenderer();
+ const hTickLabels = textTextures.value('-0123456789:.').h;
+ const isVerticalAxis = (labelSide === Side.LEFT || labelSide === Side.RIGHT);
- marksProgram_a_VCoord.disable( gl );
- marksProgram.endUse( gl );
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+
+ const sizePixels = isVerticalAxis ? viewport.h : viewport.w;
+ if (sizePixels === 0) {
+ return;
+ }
+
+ const tickTimes_PMILLIS = getTickTimes_PMILLIS(timeAxis, sizePixels, tickSpacing, tickTimeZone, referenceDate_PMILLIS);
+ const tickInterval_MILLIS = getTickInterval_MILLIS(tickTimes_PMILLIS);
+ const tickCount = tickTimes_PMILLIS.length;
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
+ // Tick marks
+ //
+
+ marksProgram.use(gl);
+ marksProgram_u_VMin.setData(gl, timeAxis.vMin);
+ marksProgram_u_VSize.setData(gl, timeAxis.vSize);
+ marksProgram_u_ViewportSize.setData(gl, viewport.w, viewport.h);
+ marksProgram_u_MarkSize.setData(gl, tickSize);
+ marksProgram_u_Color.setData(gl, tickColor);
+
+ markCoords = ensureCapacityFloat32(markCoords, 4 * tickCount);
+ for (let n = 0; n < tickCount; n++) {
+ const v = timeAxis.vAtTime(tickTimes_PMILLIS[n]);
+ markCoords[(4 * n + 0)] = v;
+ markCoords[(4 * n + 1)] = 0;
+ markCoords[(4 * n + 2)] = v;
+ markCoords[(4 * n + 3)] = 1;
+ }
+ markCoordsBuffer.setData(markCoords.subarray(0, 4 * tickCount));
+ marksProgram_a_VCoord.setDataAndEnable(gl, markCoordsBuffer, 2, GL.FLOAT);
- // Tick labels
- //
+ // IE does not support lineWidths other than 1, so make sure all browsers use lineWidth of 1
+ gl.lineWidth(1);
+ gl.drawArrays(GL.LINES, 0, 2 * tickCount);
- var ticks : TickDisplayData = getTickDisplayData( tickInterval_MILLIS, referenceDate_PMILLIS, displayTimeZone, isFuturePositive );
+ marksProgram_a_VCoord.disable(gl);
+ marksProgram.endUse(gl);
- textTextures.resetTouches( );
- textureRenderer.begin( gl, viewport );
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
- for ( var n = 0; n < tickCount; n++ ) {
- var tickTime_PMILLIS = tickTimes_PMILLIS[ n ];
- var tFrac = timeAxis.tFrac( tickTime_PMILLIS );
- if ( tFrac < 0 || tFrac >= 1 ) continue;
- var tickLabel : string = ticks.tickFormat( tickTime_PMILLIS );
- var textTexture = textTextures.value( tickLabel );
+ // Tick labels
+ //
+
+ const ticks: TickDisplayData = getTickDisplayData(tickInterval_MILLIS, referenceDate_PMILLIS, displayTimeZone, isFuturePositive, timeAxisFormat);
+
+ textTextures.resetTouches();
+ textureRenderer.begin(gl, viewport);
+
+ for (let n = 0; n < tickCount; n++) {
+ const tickTime_PMILLIS = tickTimes_PMILLIS[n];
+ const tFrac = timeAxis.tFrac(tickTime_PMILLIS);
+ if (tFrac < 0 || tFrac >= 1) {
+ continue;
+ }
+
+ const tickLabel: string = ticks.tickFormat(tickTime_PMILLIS);
+ const textTexture = textTextures.value(tickLabel);
- var xFrac : number;
- var yFrac : number;
- if ( labelSide === Side.LEFT || labelSide === Side.RIGHT ) {
- var yAnchor = textTexture.yAnchor( 0.43 );
- var j0 = ( tFrac * viewport.h ) - yAnchor*textTexture.h;
- var j = clamp( 0, viewport.h - textTexture.h, j0 );
- yFrac = j / viewport.h;
+ let xFrac: number;
+ let yFrac: number;
+ if (labelSide === Side.LEFT || labelSide === Side.RIGHT) {
+ const yAnchor = textTexture.yAnchor(0.43);
+ const j0 = (tFrac * viewport.h) - yAnchor * textTexture.h;
+ const j = clamp(0, viewport.h - textTexture.h, j0);
+ yFrac = j / viewport.h;
- if ( labelSide === Side.LEFT ) {
- xFrac = ( viewport.w - tickSize - 2 - textTexture.w ) / viewport.w;
- }
- else {
- xFrac = ( tickSize + 2 ) / viewport.w;
- }
+ if (labelSide === Side.LEFT) {
+ xFrac = (viewport.w - tickSize - 2 - textTexture.w) / viewport.w;
}
else {
- var xAnchor = 0.45;
- var i0 = ( tFrac * viewport.w ) - xAnchor*( textTexture.w );
- var i = clamp( 0, viewport.w - textTexture.w, i0 );
- xFrac = i / viewport.w;
-
- if ( labelSide === Side.BOTTOM ) {
- yFrac = ( viewport.h - tickSize - 2 - hTickLabels ) / viewport.h;
- }
- else {
- yFrac = ( tickSize + 2 ) / viewport.h;
- }
+ xFrac = (tickSize + 2) / viewport.w;
}
- textureRenderer.draw( gl, textTexture, xFrac, yFrac, { xAnchor: 0, yAnchor: 0 } );
}
+ else {
+ const xAnchor = 0.45;
+ const i0 = (tFrac * viewport.w) - xAnchor * (textTexture.w);
+ const i = clamp(0, viewport.w - textTexture.w, i0);
+ xFrac = i / viewport.w;
-
- // Axis label
- //
-
- if ( ticks.timeStructFactory ) {
- var timeStructs = createTimeStructs( timeAxis, ticks.timeStructFactory, tickTimeZone, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign );
- for ( var n = 0 ; n < timeStructs.length ; n++ ) {
- var timeStruct = timeStructs[ n ];
- var text = ticks.prefixFormat( timeStruct );
- var textTexture = textTextures.value( text );
-
- var halfTextFrac = 0.5 * textTexture.w / viewport.w;
- var minFrac = timeAxis.tFrac( timeStruct.start_PMILLIS ) - halfTextFrac;
- var maxFrac = timeAxis.tFrac( timeStruct.end_PMILLIS ) + halfTextFrac;
- var tFrac = clamp( minFrac, maxFrac, timeAxis.tFrac( timeStruct.textCenter_PMILLIS ) );
- if ( tFrac-halfTextFrac < 0 || tFrac+halfTextFrac > 1 ) continue;
-
- var xFrac : number;
- var yFrac : number;
- var textOpts : TextureDrawOptions;
- if ( labelSide === Side.LEFT || labelSide === Side.RIGHT ) {
- // Using hTickLabels here works out about right, even though the tick-label text is horizontal
- var xFrac0 = 0.5 * ( viewport.w - tickSize - 2 - hTickLabels ) / viewport.w;
- xFrac = ( labelSide === Side.LEFT ? xFrac0 : 1 - xFrac0 );
- yFrac = tFrac;
- textOpts = { xAnchor: textTexture.yAnchor( 0.5 ),
- yAnchor: 0.5,
- rotation_CCWRAD: 0.5 * Math.PI };
- }
- else {
- var yFrac0 = 0.5 * ( viewport.h - tickSize - 2 - hTickLabels ) / viewport.h;
- yFrac = ( labelSide === Side.BOTTOM ? yFrac0 : 1 - yFrac0 );
- xFrac = tFrac;
- textOpts = { xAnchor: 0.5,
- yAnchor: textTexture.yAnchor( 0.5 ),
- rotation_CCWRAD: 0 };
- }
- textureRenderer.draw( gl, textTexture, xFrac, yFrac, textOpts );
+ if (labelSide === Side.BOTTOM) {
+ yFrac = (viewport.h - tickSize - 2 - hTickLabels) / viewport.h;
+ }
+ else {
+ yFrac = (tickSize + 2) / viewport.h;
}
}
+ textureRenderer.draw(gl, textTexture, xFrac, yFrac, { xAnchor: 0, yAnchor: 0 });
+ }
- // Finish up
- //
+ // Axis label
+ //
- textureRenderer.end( gl );
- textTextures.retainTouched( );
- }
- }
+ if (ticks.timeStructFactory) {
+ const timeStructs = createTimeStructs(timeAxis, ticks.timeStructFactory, tickTimeZone, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign);
+ for (let n = 0; n < timeStructs.length; n++) {
+ const timeStruct = timeStructs[n];
+ const text = ticks.prefixFormat(timeStruct);
+ const textTexture = textTextures.value(text);
- function getTickDisplayData( tickInterval_MILLIS : number, referenceDate_PMILLIS : number, displayTimeZone : string, isFuturePositive : boolean ) : TickDisplayData {
- if ( hasval( referenceDate_PMILLIS ) ) {
- return getTickDisplayDataRelative( tickInterval_MILLIS, referenceDate_PMILLIS, isFuturePositive );
- }
- else {
- return getTickDisplayDataAbsolute( tickInterval_MILLIS, displayTimeZone );
- }
- }
+ const halfTextFrac = 0.5 * textTexture.w / viewport.w;
+ const minFrac = timeAxis.tFrac(timeStruct.start_PMILLIS) - halfTextFrac;
+ const maxFrac = timeAxis.tFrac(timeStruct.end_PMILLIS) + halfTextFrac;
+ const tFrac = clamp(minFrac, maxFrac, timeAxis.tFrac(timeStruct.textCenter_PMILLIS));
+ if (tFrac - halfTextFrac < 0 || tFrac + halfTextFrac > 1) {
+ continue;
+ }
- function getTickDisplayDataRelative( tickInterval_MILLIS : number, referenceDate_PMILLIS : number, isFuturePositive : boolean ) : TickDisplayData {
- if ( tickInterval_MILLIS <= minutesToMillis( 1 ) ) {
- var tickFormat : TickFormat = function( tickTime_PMILLIS : number ) : string {
- var elapsedTime_MILLIS = Math.abs( tickTime_PMILLIS - referenceDate_PMILLIS );
- var elapsedTime_DAYS = millisToDays( elapsedTime_MILLIS );
- var elapsedTime_DAYS_WHOLE = Math.floor( elapsedTime_DAYS );
- var elapsedTime_HOURS = ( elapsedTime_DAYS - elapsedTime_DAYS_WHOLE ) * 24;
- var elapsedTime_HOURS_WHOLE = Math.floor( elapsedTime_HOURS );
- var elapsedTime_MIN = ( elapsedTime_HOURS - elapsedTime_HOURS_WHOLE ) * 60;
- var elapsedTime_MIN_WHOLE = Math.floor( elapsedTime_MIN );
- var elapsedTime_SEC = ( elapsedTime_MIN - elapsedTime_MIN_WHOLE ) * 60;
- // use round() here instead of floor() because we always expect ticks to be on even second
- // boundaries but rounding error will cause us to be somewhat unpredictably above or below
- // the nearest even second boundary
- var elapsedTime_SEC_WHOLE = Math.round( elapsedTime_SEC );
- // however the above fails when we round up to a whole minute, so special case that
- if ( elapsedTime_SEC_WHOLE >= 60 )
- {
- elapsedTime_SEC_WHOLE -= 60;
- elapsedTime_MIN_WHOLE += 1;
+ let xFrac: number;
+ let yFrac: number;
+ let textOpts: TextureDrawOptions;
+ if (labelSide === Side.LEFT || labelSide === Side.RIGHT) {
+ // Using hTickLabels here works out about right, even though the tick-label text is horizontal
+ const xFrac0 = 0.5 * (viewport.w - tickSize - 2 - hTickLabels) / viewport.w;
+ xFrac = (labelSide === Side.LEFT ? xFrac0 : 1 - xFrac0);
+ yFrac = tFrac;
+ textOpts = {
+ xAnchor: textTexture.yAnchor(0.5),
+ yAnchor: 0.5,
+ rotation_CCWRAD: 0.5 * Math.PI
+ };
}
- if ( elapsedTime_MIN_WHOLE >= 60 )
- {
- elapsedTime_MIN_WHOLE = 0;
+ else {
+ const yFrac0 = 0.5 * (viewport.h - tickSize - 2 - hTickLabels) / viewport.h;
+ yFrac = (labelSide === Side.BOTTOM ? yFrac0 : 1 - yFrac0);
+ xFrac = tFrac;
+ textOpts = {
+ xAnchor: 0.5,
+ yAnchor: textTexture.yAnchor(0.5),
+ rotation_CCWRAD: 0
+ };
}
+ textureRenderer.draw(gl, textTexture, xFrac, yFrac, textOpts);
+ }
+ }
- var min : string = elapsedTime_MIN_WHOLE < 10 ? '0' + elapsedTime_MIN_WHOLE : '' + elapsedTime_MIN_WHOLE;
- var sec : string = elapsedTime_SEC_WHOLE < 10 ? '0' + elapsedTime_SEC_WHOLE : '' + elapsedTime_SEC_WHOLE;
-
- return min + ':' + sec;
- };
- var prefixFormat = function( timeStruct : TimeStruct ) : string {
- var center_PMILLIS = ( timeStruct.end_PMILLIS - timeStruct.start_PMILLIS ) / 2 + timeStruct.start_PMILLIS;
- var elapsedTime_MILLIS = center_PMILLIS - referenceDate_PMILLIS;
- var negative = ( elapsedTime_MILLIS < 0 );
- var signString = ( negative && isFuturePositive ) || ( !negative && !isFuturePositive) ? "-" : "";
- elapsedTime_MILLIS = Math.abs( elapsedTime_MILLIS );
+ // Finish up
+ //
- var elapsedTime_DAYS = millisToDays( elapsedTime_MILLIS );
- var elapsedTime_DAYS_WHOLE = Math.floor( elapsedTime_DAYS );
- var elapsedTime_HOURS = ( elapsedTime_DAYS - elapsedTime_DAYS_WHOLE ) * 24;
- var elapsedTime_HOURS_WHOLE = Math.floor( elapsedTime_HOURS );
+ textureRenderer.end(gl);
+ textTextures.retainTouched();
+ };
+}
- return 'Day ' + signString + elapsedTime_DAYS_WHOLE + ' Hour ' + signString + elapsedTime_HOURS_WHOLE;
- };
+function getTickDisplayData(tickInterval_MILLIS: number, referenceDate_PMILLIS: number, displayTimeZone: string, isFuturePositive: boolean, timeAxisFormat: TimeAxisFormatOptions): TickDisplayData {
+ if (hasval(referenceDate_PMILLIS)) {
+ return getTickDisplayDataRelative(tickInterval_MILLIS, referenceDate_PMILLIS, isFuturePositive);
+ }
+ else {
+ return getTickDisplayDataAbsolute(tickInterval_MILLIS, displayTimeZone, timeAxisFormat);
+ }
+}
- var timeStructFactory = function( ) : TimeStruct { return new TimeStruct( ) };
- }
- else if ( tickInterval_MILLIS <= hoursToMillis( 12 ) ) {
- var tickFormat : TickFormat = function( tickTime_PMILLIS : number ) : string {
- var elapsedTime_MILLIS = Math.abs( tickTime_PMILLIS - referenceDate_PMILLIS );
- var elapsedTime_DAYS = millisToDays( elapsedTime_MILLIS );
- var elapsedTime_DAYS_WHOLE = Math.floor( elapsedTime_DAYS );
- var elapsedTime_HOURS = ( elapsedTime_DAYS - elapsedTime_DAYS_WHOLE ) * 24;
- var elapsedTime_HOURS_WHOLE = Math.floor( elapsedTime_HOURS );
- var elapsedTime_MIN = ( elapsedTime_HOURS - elapsedTime_HOURS_WHOLE ) * 60;
- // use round() here instead of floor() because we always expect ticks to be on even minute
- // boundaries but rounding error will cause us to be somewhat unpredictably above or below
- // the nearest even minute boundary
- var elapsedTime_MIN_WHOLE = Math.round( elapsedTime_MIN );
- // however the above fails when we round up to a whole hour, so special case that
- if ( elapsedTime_MIN_WHOLE >= 60 )
- {
- elapsedTime_MIN_WHOLE -= 60;
- elapsedTime_HOURS_WHOLE += 1;
- }
- if ( elapsedTime_HOURS_WHOLE >= 24 )
- {
- elapsedTime_HOURS_WHOLE = 0;
- }
+function getTickDisplayDataRelative(tickInterval_MILLIS: number, referenceDate_PMILLIS: number, isFuturePositive: boolean): TickDisplayData {
+ let tickFormat: TickFormat = null;
+ let prefixFormat: PrefixFormat = null;
+ let timeStructFactory: TimeStructFactory = null;
+ if (tickInterval_MILLIS <= minutesToMillis(1)) {
+ tickFormat = function (tickTime_PMILLIS: number): string {
+ const elapsedTime_MILLIS = Math.abs(tickTime_PMILLIS - referenceDate_PMILLIS);
+ const elapsedTime_DAYS = millisToDays(elapsedTime_MILLIS);
+ const elapsedTime_DAYS_WHOLE = Math.floor(elapsedTime_DAYS);
+ const elapsedTime_HOURS = (elapsedTime_DAYS - elapsedTime_DAYS_WHOLE) * 24;
+ const elapsedTime_HOURS_WHOLE = Math.floor(elapsedTime_HOURS);
+ const elapsedTime_MIN = (elapsedTime_HOURS - elapsedTime_HOURS_WHOLE) * 60;
+ let elapsedTime_MIN_WHOLE = Math.floor(elapsedTime_MIN);
+ const elapsedTime_SEC = (elapsedTime_MIN - elapsedTime_MIN_WHOLE) * 60;
+ // use round() here instead of floor() because we always expect ticks to be on even second
+ // boundaries but rounding error will cause us to be somewhat unpredictably above or below
+ // the nearest even second boundary
+ let elapsedTime_SEC_WHOLE = Math.round(elapsedTime_SEC);
+ // however the above fails when we round up to a whole minute, so special case that
+ if (elapsedTime_SEC_WHOLE >= 60) {
+ elapsedTime_SEC_WHOLE -= 60;
+ elapsedTime_MIN_WHOLE += 1;
+ }
+ if (elapsedTime_MIN_WHOLE >= 60) {
+ elapsedTime_MIN_WHOLE = 0;
+ }
- var hour : string = elapsedTime_HOURS_WHOLE < 10 ? '0' + elapsedTime_HOURS_WHOLE : '' + elapsedTime_HOURS_WHOLE;
- var min : string = elapsedTime_MIN_WHOLE < 10 ? '0' + elapsedTime_MIN_WHOLE : '' + elapsedTime_MIN_WHOLE;
+ const min: string = elapsedTime_MIN_WHOLE < 10 ? '0' + elapsedTime_MIN_WHOLE : '' + elapsedTime_MIN_WHOLE;
+ const sec: string = elapsedTime_SEC_WHOLE < 10 ? '0' + elapsedTime_SEC_WHOLE : '' + elapsedTime_SEC_WHOLE;
+
+ return min + ':' + sec;
+ };
+
+ prefixFormat = function (timeStruct: TimeStruct): string {
+ const center_PMILLIS = (timeStruct.end_PMILLIS - timeStruct.start_PMILLIS) / 2 + timeStruct.start_PMILLIS;
+ let elapsedTime_MILLIS = center_PMILLIS - referenceDate_PMILLIS;
+ const negative = (elapsedTime_MILLIS < 0);
+ const signString = (negative && isFuturePositive) || (!negative && !isFuturePositive) ? '-' : '';
+ elapsedTime_MILLIS = Math.abs(elapsedTime_MILLIS);
+
+ const elapsedTime_DAYS = millisToDays(elapsedTime_MILLIS);
+ const elapsedTime_DAYS_WHOLE = Math.floor(elapsedTime_DAYS);
+ const elapsedTime_HOURS = (elapsedTime_DAYS - elapsedTime_DAYS_WHOLE) * 24;
+ const elapsedTime_HOURS_WHOLE = Math.floor(elapsedTime_HOURS);
+
+ return 'Day ' + signString + elapsedTime_DAYS_WHOLE + ' Hour ' + signString + elapsedTime_HOURS_WHOLE;
+ };
+
+ timeStructFactory = function (): TimeStruct { return new TimeStruct(); };
+ }
+ else if (tickInterval_MILLIS <= hoursToMillis(12)) {
+ tickFormat = function (tickTime_PMILLIS: number): string {
+ const elapsedTime_MILLIS = Math.abs(tickTime_PMILLIS - referenceDate_PMILLIS);
+ const elapsedTime_DAYS = millisToDays(elapsedTime_MILLIS);
+ const elapsedTime_DAYS_WHOLE = Math.floor(elapsedTime_DAYS);
+ const elapsedTime_HOURS = (elapsedTime_DAYS - elapsedTime_DAYS_WHOLE) * 24;
+ let elapsedTime_HOURS_WHOLE = Math.floor(elapsedTime_HOURS);
+ const elapsedTime_MIN = (elapsedTime_HOURS - elapsedTime_HOURS_WHOLE) * 60;
+ // use round() here instead of floor() because we always expect ticks to be on even minute
+ // boundaries but rounding error will cause us to be somewhat unpredictably above or below
+ // the nearest even minute boundary
+ let elapsedTime_MIN_WHOLE = Math.round(elapsedTime_MIN);
+ // however the above fails when we round up to a whole hour, so special case that
+ if (elapsedTime_MIN_WHOLE >= 60) {
+ elapsedTime_MIN_WHOLE -= 60;
+ elapsedTime_HOURS_WHOLE += 1;
+ }
+ if (elapsedTime_HOURS_WHOLE >= 24) {
+ elapsedTime_HOURS_WHOLE = 0;
+ }
- return hour + ':' + min;
- };
+ const hour: string = elapsedTime_HOURS_WHOLE < 10 ? '0' + elapsedTime_HOURS_WHOLE : '' + elapsedTime_HOURS_WHOLE;
+ const min: string = elapsedTime_MIN_WHOLE < 10 ? '0' + elapsedTime_MIN_WHOLE : '' + elapsedTime_MIN_WHOLE;
- var prefixFormat = function( timeStruct : TimeStruct ) : string {
- var center_PMILLIS = ( timeStruct.end_PMILLIS - timeStruct.start_PMILLIS ) / 2 + timeStruct.start_PMILLIS;
- var elapsedTime_MILLIS = center_PMILLIS - referenceDate_PMILLIS;
- var negative = ( elapsedTime_MILLIS < 0 );
- var signString = ( negative && isFuturePositive ) || ( !negative && !isFuturePositive) ? "-" : "";
- elapsedTime_MILLIS = Math.abs( elapsedTime_MILLIS );
- var elapsedTime_DAYS = Math.floor( millisToDays( elapsedTime_MILLIS ) );
- return 'Day ' + signString + elapsedTime_DAYS;
- };
+ return hour + ':' + min;
+ };
- var timeStructFactory = function( ) : TimeStruct { return new TimeStruct( ) };
- }
- else {
- var tickFormat : TickFormat = function( tickTime_PMILLIS : number ) : string {
- var elapsedTime_MILLIS = tickTime_PMILLIS - referenceDate_PMILLIS;
- var negative = ( elapsedTime_MILLIS < 0 );
- var signString = ( negative && isFuturePositive ) || ( !negative && !isFuturePositive) ? "-" : "";
- elapsedTime_MILLIS = Math.abs( elapsedTime_MILLIS );
- var elapsedTime_DAYS = Math.floor( millisToDays( elapsedTime_MILLIS ) );
- return elapsedTime_DAYS === 0 ? '' + elapsedTime_DAYS : signString + elapsedTime_DAYS;
- };
- }
+ prefixFormat = function (timeStruct: TimeStruct): string {
+ const center_PMILLIS = (timeStruct.end_PMILLIS - timeStruct.start_PMILLIS) / 2 + timeStruct.start_PMILLIS;
+ let elapsedTime_MILLIS = center_PMILLIS - referenceDate_PMILLIS;
+ const negative = (elapsedTime_MILLIS < 0);
+ const signString = (negative && isFuturePositive) || (!negative && !isFuturePositive) ? '-' : '';
+ elapsedTime_MILLIS = Math.abs(elapsedTime_MILLIS);
+ const elapsedTime_DAYS = Math.floor(millisToDays(elapsedTime_MILLIS));
+ return 'Day ' + signString + elapsedTime_DAYS;
+ };
- return { prefixFormat: prefixFormat, tickFormat: tickFormat, timeStructFactory:timeStructFactory };
+ timeStructFactory = function (): TimeStruct { return new TimeStruct(); };
+ }
+ else {
+ tickFormat = function (tickTime_PMILLIS: number): string {
+ let elapsedTime_MILLIS = tickTime_PMILLIS - referenceDate_PMILLIS;
+ const negative = (elapsedTime_MILLIS < 0);
+ const signString = (negative && isFuturePositive) || (!negative && !isFuturePositive) ? '-' : '';
+ elapsedTime_MILLIS = Math.abs(elapsedTime_MILLIS);
+ const elapsedTime_DAYS = Math.floor(millisToDays(elapsedTime_MILLIS));
+ return elapsedTime_DAYS === 0 ? '' + elapsedTime_DAYS : signString + elapsedTime_DAYS;
+ };
}
- function getTickDisplayDataAbsolute( tickInterval_MILLIS : number, displayTimeZone : string ) : TickDisplayData {
+ return { prefixFormat: prefixFormat, tickFormat: tickFormat, timeStructFactory: timeStructFactory };
+}
- var defaultTickFormat = function( format : string ) : TickFormat { return function( tickTime_PMILLIS : number ) : string { return moment( tickTime_PMILLIS ).zone( displayTimeZone ).format( format ) } };
- var defaultPrefixFormat = function( format : string ) : PrefixFormat { return function( timeStruct : TimeStruct ) : string { return moment( timeStruct.textCenter_PMILLIS ).zone( displayTimeZone ).format( format ) } };
+function getTickDisplayDataAbsolute(tickInterval_MILLIS: number, displayTimeZone: string, timeAxisFormat: TimeAxisFormatOptions): TickDisplayData {
+
+ let tickFormat: TickFormat = null;
+ let prefixFormat: PrefixFormat = null;
+ let timeStructFactory: TimeStructFactory = null;
+ const defaultTickFormat = function (format: string): TickFormat { return function (tickTime_PMILLIS: number): string { return moment(tickTime_PMILLIS).zone(displayTimeZone).format(format); }; };
+ const defaultPrefixFormat = function (format: string): PrefixFormat { return function (timeStruct: TimeStruct): string { return moment(timeStruct.textCenter_PMILLIS).zone(displayTimeZone).format(format); }; };
+
+ if (tickInterval_MILLIS <= minutesToMillis(1)) {
+ const formatOptions = new FormatOptions('mm:ss', 'D MMM HH:00');
+ const hour = (hasval(timeAxisFormat) && hasval(timeAxisFormat.hour) ? timeAxisFormat.hour : formatOptions);
+ tickFormat = defaultTickFormat((hasval(hour.tickFormat) && hour.tickFormat !== '' ? hour.tickFormat : formatOptions.tickFormat));
+ prefixFormat = defaultPrefixFormat((hasval(hour.prefixFormat) && hour.prefixFormat !== '' ? hour.prefixFormat : formatOptions.prefixFormat));
+ timeStructFactory = function (): TimeStruct { return new HourStruct(); };
+ }
+ else if (tickInterval_MILLIS <= hoursToMillis(12)) {
+ const formatOptions = new FormatOptions('HH:mm', 'D MMM YYYY');
+ const day = (hasval(timeAxisFormat) && hasval(timeAxisFormat.day) ? timeAxisFormat.day : formatOptions);
+ tickFormat = defaultTickFormat((hasval(day.tickFormat) && day.tickFormat !== '' ? day.tickFormat : formatOptions.tickFormat));
+ prefixFormat = defaultPrefixFormat((hasval(day.prefixFormat) && day.prefixFormat !== '' ? day.prefixFormat : formatOptions.prefixFormat));
+ timeStructFactory = function (): TimeStruct { return new DayStruct(); };
+ }
+ else if (tickInterval_MILLIS <= daysToMillis(10)) {
+ const formatOptions = new FormatOptions('D', 'MMM YYYY');
+ const month = (hasval(timeAxisFormat) && hasval(timeAxisFormat.month) ? timeAxisFormat.month : formatOptions);
+ tickFormat = defaultTickFormat((hasval(month.tickFormat) && month.tickFormat !== '' ? month.tickFormat : formatOptions.tickFormat));
+ prefixFormat = defaultPrefixFormat((hasval(month.prefixFormat) && month.prefixFormat !== '' ? month.prefixFormat : formatOptions.prefixFormat));
+ timeStructFactory = function (): TimeStruct { return new MonthStruct(); };
+ }
+ else if (tickInterval_MILLIS <= daysToMillis(60)) {
+ const formatOptions = new FormatOptions('MMM', 'YYYY');
+ const year = (hasval(timeAxisFormat) && hasval(timeAxisFormat.year) ? timeAxisFormat.year : formatOptions);
+ tickFormat = defaultTickFormat((hasval(year.tickFormat) && year.tickFormat !== '' ? year.tickFormat : formatOptions.tickFormat));
+ prefixFormat = defaultPrefixFormat((hasval(year.prefixFormat) && year.prefixFormat !== '' ? year.prefixFormat : formatOptions.prefixFormat));
+ timeStructFactory = function (): TimeStruct { return new YearStruct(); };
+ }
+ else {
+ tickFormat = defaultTickFormat('YYYY');
+ }
+
+ return { prefixFormat: prefixFormat, tickFormat: tickFormat, timeStructFactory: timeStructFactory };
+}
- if ( tickInterval_MILLIS <= minutesToMillis( 1 ) ) {
- var tickFormat = defaultTickFormat( 'mm:ss' );
- var prefixFormat = defaultPrefixFormat( 'D MMM HH:00' );
- var timeStructFactory = function( ) : TimeStruct { return new HourStruct( ) };
- }
- else if ( tickInterval_MILLIS <= hoursToMillis( 12 ) ) {
- var tickFormat = defaultTickFormat( 'HH:mm' );
- var prefixFormat = defaultPrefixFormat( 'D MMM YYYY' );
- var timeStructFactory = function( ) : TimeStruct { return new DayStruct( ) };
- }
- else if ( tickInterval_MILLIS <= daysToMillis( 10 ) ) {
- var tickFormat = defaultTickFormat( 'D' );
- var prefixFormat = defaultPrefixFormat( 'MMM YYYY' );
- var timeStructFactory = function( ) : TimeStruct { return new MonthStruct( ) };
- }
- else if ( tickInterval_MILLIS <= daysToMillis( 60 ) ) {
- var tickFormat = defaultTickFormat( 'MMM' );
- var prefixFormat = defaultPrefixFormat( 'YYYY' );
- var timeStructFactory = function( ) : TimeStruct { return new YearStruct( ) };
- }
- else {
- var tickFormat = defaultTickFormat( 'YYYY' );
- }
+interface TickDisplayData {
+ prefixFormat: PrefixFormat;
+ tickFormat: TickFormat;
+ timeStructFactory: TimeStructFactory;
+}
- return { prefixFormat: prefixFormat, tickFormat: tickFormat, timeStructFactory:timeStructFactory };
- }
+type PrefixFormat = (timeStruct: TimeStruct) => string;
- interface TickDisplayData {
- prefixFormat : PrefixFormat;
- tickFormat : TickFormat;
- timeStructFactory : TimeStructFactory;
- }
+type TickFormat = (tickTime_PMILLIS: number) => string;
- interface PrefixFormat {
- ( timeStruct : TimeStruct ) : string;
- }
+type TimeStructFactory = () => TimeStruct;
- interface TickFormat {
- ( tickTime_PMILLIS : number ) : string;
- }
+class TimeStruct {
+ public start_PMILLIS: number;
+ public end_PMILLIS: number;
+ public viewStart_PMILLIS: number;
+ public viewEnd_PMILLIS: number;
+ public textCenter_PMILLIS: number;
- interface TimeStructFactory {
- ( ): TimeStruct;
+ setTime(time_PMILLIS: number, timeZone: string): Moment {
+ return moment(time_PMILLIS).zone(timeZone);
}
- class TimeStruct {
- public start_PMILLIS : number;
- public end_PMILLIS : number;
- public viewStart_PMILLIS : number;
- public viewEnd_PMILLIS : number;
- public textCenter_PMILLIS : number;
-
- setTime( time_PMILLIS : number, timeZone : string ) : Moment {
- return moment( time_PMILLIS ).zone( timeZone );
- }
-
- incrementTime( m : Moment ) {
- }
+ incrementTime(m: Moment) {
}
+}
- class YearStruct extends TimeStruct {
- setTime( time_PMILLIS : number, timeZone : string ) : Moment {
- var m = moment( time_PMILLIS ).zone( timeZone );
- m.month( 0 );
- m.date( 0 );
- m.hours( 0 );
- m.minutes( 0 );
- m.seconds( 0 );
- return m;
- }
+class YearStruct extends TimeStruct {
+ setTime(time_PMILLIS: number, timeZone: string): Moment {
+ const m = moment(time_PMILLIS).zone(timeZone);
+ m.month(0);
+ m.date(0);
+ m.hours(0);
+ m.minutes(0);
+ m.seconds(0);
+ return m;
+ }
- incrementTime( m : Moment ) {
- m.add( 'years', 1 );
- }
+ incrementTime(m: Moment) {
+ m.add('years', 1);
}
+}
- class MonthStruct extends TimeStruct {
- setTime( time_PMILLIS : number, timeZone : string ) : Moment {
- var m = moment( time_PMILLIS ).zone( timeZone );
- m.date( 0 );
- m.hours( 0 );
- m.minutes( 0 );
- m.seconds( 0 );
- return m;
- }
+class MonthStruct extends TimeStruct {
+ setTime(time_PMILLIS: number, timeZone: string): Moment {
+ const m = moment(time_PMILLIS).zone(timeZone);
+ m.date(0);
+ m.hours(0);
+ m.minutes(0);
+ m.seconds(0);
+ return m;
+ }
- incrementTime( m : Moment ) {
- m.add( 'months', 1 );
- }
+ incrementTime(m: Moment) {
+ m.add('months', 1);
}
+}
- class DayStruct extends TimeStruct {
- setTime( time_PMILLIS : number, timeZone : string ) : Moment {
- var m = moment( time_PMILLIS ).zone( timeZone );
- m.hours( 0 );
- m.minutes( 0 );
- m.seconds( 0 );
- return m;
- }
+class DayStruct extends TimeStruct {
+ setTime(time_PMILLIS: number, timeZone: string): Moment {
+ const m = moment(time_PMILLIS).zone(timeZone);
+ m.hours(0);
+ m.minutes(0);
+ m.seconds(0);
+ return m;
+ }
- incrementTime( m : Moment ) {
- m.add( 'days', 1 );
- }
+ incrementTime(m: Moment) {
+ m.add('days', 1);
}
+}
- class HourStruct extends TimeStruct {
- setTime( time_PMILLIS : number, timeZone : string ) : Moment {
- var m = moment( time_PMILLIS ).zone( timeZone );
- m.minutes( 0 );
- m.seconds( 0 );
- return m;
- }
+class HourStruct extends TimeStruct {
+ setTime(time_PMILLIS: number, timeZone: string): Moment {
+ const m = moment(time_PMILLIS).zone(timeZone);
+ m.minutes(0);
+ m.seconds(0);
+ return m;
+ }
- incrementTime( m : Moment ) {
- m.add( 'hours', 1 );
- }
+ incrementTime(m: Moment) {
+ m.add('hours', 1);
}
+}
- function createTimeStructs( timeAxis : TimeAxis1D, factory : TimeStructFactory, timeZone : string, referenceDate_PMILLIS : number, isFuturePositive : boolean, tickTimes_PMILLIS : number[], labelAlign : number ) : TimeStruct[] {
- if ( hasval( referenceDate_PMILLIS ) ) {
- var tickInterval_MILLIS = getTickInterval_MILLIS( tickTimes_PMILLIS );
+function createTimeStructs(timeAxis: TimeAxis1D, factory: TimeStructFactory, timeZone: string, referenceDate_PMILLIS: number, isFuturePositive: boolean, tickTimes_PMILLIS: number[], labelAlign: number): TimeStruct[] {
+ if (hasval(referenceDate_PMILLIS)) {
+ const tickInterval_MILLIS = getTickInterval_MILLIS(tickTimes_PMILLIS);
- if ( tickInterval_MILLIS <= minutesToMillis( 1 ) ) {
- return createTimeStructsRelativeHours( timeAxis, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign );
- }
- else {
- return createTimeStructsRelativeDays( timeAxis, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign );
- }
+ if (tickInterval_MILLIS <= minutesToMillis(1)) {
+ return createTimeStructsRelativeHours(timeAxis, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign);
}
else {
- return createTimeStructsAbsolute( timeAxis, factory, timeZone, tickTimes_PMILLIS, labelAlign );
+ return createTimeStructsRelativeDays(timeAxis, referenceDate_PMILLIS, isFuturePositive, tickTimes_PMILLIS, labelAlign);
}
}
+ else {
+ return createTimeStructsAbsolute(timeAxis, factory, timeZone, tickTimes_PMILLIS, labelAlign);
+ }
+}
- function createTimeStructsRelativeHours( timeAxis : TimeAxis1D, referenceDate_PMILLIS : number, isFuturePositive : boolean, tickTimes_PMILLIS : number[], labelAlign : number ) : TimeStruct[] {
-
- var dMin_PMILLIS = timeAxis.tMin_PMILLIS;
- var dMax_PMILLIS = timeAxis.tMax_PMILLIS;
-
- var timeStructs : TimeStruct[] = [];
- var maxViewDuration_MILLIS = Number.NEGATIVE_INFINITY;
+function createTimeStructsRelativeHours(timeAxis: TimeAxis1D, referenceDate_PMILLIS: number, isFuturePositive: boolean, tickTimes_PMILLIS: number[], labelAlign: number): TimeStruct[] {
- var previous_HOURS = null;
- var previous_SIGN = null;
+ const dMin_PMILLIS = timeAxis.tMin_PMILLIS;
+ const dMax_PMILLIS = timeAxis.tMax_PMILLIS;
- for ( var n = 0; n < tickTimes_PMILLIS.length; n++ ) {
+ const timeStructs: TimeStruct[] = [];
+ let maxViewDuration_MILLIS = Number.NEGATIVE_INFINITY;
- var elapsedTime_MILLIS = tickTimes_PMILLIS[n] - referenceDate_PMILLIS;
- var negative = ( elapsedTime_MILLIS < 0 );
- var signString = ( negative && isFuturePositive ) || ( !negative && !isFuturePositive) ? "-" : "";
- elapsedTime_MILLIS = Math.abs( elapsedTime_MILLIS );
- var elapsedTime_HOURS = millisToHours( elapsedTime_MILLIS );
- var elapsedTime_HOURS_WHOLE = Math.floor( elapsedTime_HOURS );
+ let previous_HOURS = null;
+ let previous_SIGN = null;
- if ( hasval( previous_HOURS ) && elapsedTime_HOURS_WHOLE === previous_HOURS && negative === previous_SIGN ) continue;
- previous_HOURS = elapsedTime_HOURS_WHOLE;
- previous_SIGN = negative;
+ for (let n = 0; n < tickTimes_PMILLIS.length; n++) {
- var timeStruct = new TimeStruct( );
+ let elapsedTime_MILLIS = tickTimes_PMILLIS[n] - referenceDate_PMILLIS;
+ const negative = (elapsedTime_MILLIS < 0);
+ const signString = (negative && isFuturePositive) || (!negative && !isFuturePositive) ? '-' : '';
+ elapsedTime_MILLIS = Math.abs(elapsedTime_MILLIS);
+ const elapsedTime_HOURS = millisToHours(elapsedTime_MILLIS);
+ const elapsedTime_HOURS_WHOLE = Math.floor(elapsedTime_HOURS);
- if ( negative ) {
- timeStruct.end_PMILLIS = hoursToMillis( -elapsedTime_HOURS_WHOLE ) + referenceDate_PMILLIS;
- timeStruct.start_PMILLIS = timeStruct.end_PMILLIS - hoursToMillis( 1 );
- }
- else {
- timeStruct.start_PMILLIS = hoursToMillis( elapsedTime_HOURS_WHOLE ) + referenceDate_PMILLIS;
- timeStruct.end_PMILLIS = timeStruct.start_PMILLIS + hoursToMillis( 1 );
- }
+ if (hasval(previous_HOURS) && elapsedTime_HOURS_WHOLE === previous_HOURS && negative === previous_SIGN) {
+ continue;
+ }
+ previous_HOURS = elapsedTime_HOURS_WHOLE;
+ previous_SIGN = negative;
- timeStruct.viewStart_PMILLIS = clamp( timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMin_PMILLIS );
- timeStruct.viewEnd_PMILLIS = clamp( timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMax_PMILLIS );
+ const timeStruct = new TimeStruct();
- maxViewDuration_MILLIS = Math.max( maxViewDuration_MILLIS, timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS );
-
- timeStructs.push( timeStruct );
+ if (negative) {
+ timeStruct.end_PMILLIS = hoursToMillis(-elapsedTime_HOURS_WHOLE) + referenceDate_PMILLIS;
+ timeStruct.start_PMILLIS = timeStruct.end_PMILLIS - hoursToMillis(1);
}
+ else {
+ timeStruct.start_PMILLIS = hoursToMillis(elapsedTime_HOURS_WHOLE) + referenceDate_PMILLIS;
+ timeStruct.end_PMILLIS = timeStruct.start_PMILLIS + hoursToMillis(1);
+ }
+
+ timeStruct.viewStart_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMin_PMILLIS);
+ timeStruct.viewEnd_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMax_PMILLIS);
- setTimeStructTextCenter( timeStructs, labelAlign, maxViewDuration_MILLIS );
+ maxViewDuration_MILLIS = Math.max(maxViewDuration_MILLIS, timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS);
- return timeStructs;
+ timeStructs.push(timeStruct);
}
- function createTimeStructsRelativeDays( timeAxis : TimeAxis1D, referenceDate_PMILLIS : number, isFuturePositive : boolean, tickTimes_PMILLIS : number[], labelAlign : number ) : TimeStruct[] {
+ setTimeStructTextCenter(timeStructs, labelAlign, maxViewDuration_MILLIS);
- var dMin_PMILLIS = timeAxis.tMin_PMILLIS;
- var dMax_PMILLIS = timeAxis.tMax_PMILLIS;
+ return timeStructs;
+}
- var timeStructs : TimeStruct[] = [];
- var maxViewDuration_MILLIS = Number.NEGATIVE_INFINITY;
+function createTimeStructsRelativeDays(timeAxis: TimeAxis1D, referenceDate_PMILLIS: number, isFuturePositive: boolean, tickTimes_PMILLIS: number[], labelAlign: number): TimeStruct[] {
- var previous_DAYS = null;
- var previous_SIGN = null;
+ const dMin_PMILLIS = timeAxis.tMin_PMILLIS;
+ const dMax_PMILLIS = timeAxis.tMax_PMILLIS;
- for ( var n = 0; n < tickTimes_PMILLIS.length; n++ ) {
+ const timeStructs: TimeStruct[] = [];
+ let maxViewDuration_MILLIS = Number.NEGATIVE_INFINITY;
- var elapsedTime_MILLIS = tickTimes_PMILLIS[n] - referenceDate_PMILLIS;
- var negative = ( elapsedTime_MILLIS < 0 );
- var signString = ( negative && isFuturePositive ) || ( !negative && !isFuturePositive) ? "-" : "";
- elapsedTime_MILLIS = Math.abs( elapsedTime_MILLIS );
- var elapsedTime_DAYS = millisToDays( elapsedTime_MILLIS );
- var elapsedTime_DAYS_WHOLE = Math.floor( elapsedTime_DAYS );
+ let previous_DAYS = null;
+ let previous_SIGN = null;
- if ( hasval( previous_DAYS ) && elapsedTime_DAYS_WHOLE === previous_DAYS && negative === previous_SIGN ) continue;
- previous_DAYS = elapsedTime_DAYS_WHOLE;
- previous_SIGN = negative;
+ for (let n = 0; n < tickTimes_PMILLIS.length; n++) {
- var timeStruct = new TimeStruct( );
+ let elapsedTime_MILLIS = tickTimes_PMILLIS[n] - referenceDate_PMILLIS;
+ const negative = (elapsedTime_MILLIS < 0);
+ const signString = (negative && isFuturePositive) || (!negative && !isFuturePositive) ? '-' : '';
+ elapsedTime_MILLIS = Math.abs(elapsedTime_MILLIS);
+ const elapsedTime_DAYS = millisToDays(elapsedTime_MILLIS);
+ const elapsedTime_DAYS_WHOLE = Math.floor(elapsedTime_DAYS);
- if ( negative ) {
- timeStruct.end_PMILLIS = daysToMillis( -elapsedTime_DAYS_WHOLE ) + referenceDate_PMILLIS;
- timeStruct.start_PMILLIS = timeStruct.end_PMILLIS - daysToMillis( 1 );
- }
- else {
- timeStruct.start_PMILLIS = daysToMillis( elapsedTime_DAYS_WHOLE ) + referenceDate_PMILLIS;
- timeStruct.end_PMILLIS = timeStruct.start_PMILLIS + daysToMillis( 1 );
- }
+ if (hasval(previous_DAYS) && elapsedTime_DAYS_WHOLE === previous_DAYS && negative === previous_SIGN) {
+ continue;
+ }
+ previous_DAYS = elapsedTime_DAYS_WHOLE;
+ previous_SIGN = negative;
- timeStruct.viewStart_PMILLIS = clamp( timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMin_PMILLIS );
- timeStruct.viewEnd_PMILLIS = clamp( timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMax_PMILLIS );
+ const timeStruct = new TimeStruct();
- maxViewDuration_MILLIS = Math.max( maxViewDuration_MILLIS, timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS );
-
- timeStructs.push( timeStruct );
+ if (negative) {
+ timeStruct.end_PMILLIS = daysToMillis(-elapsedTime_DAYS_WHOLE) + referenceDate_PMILLIS;
+ timeStruct.start_PMILLIS = timeStruct.end_PMILLIS - daysToMillis(1);
+ }
+ else {
+ timeStruct.start_PMILLIS = daysToMillis(elapsedTime_DAYS_WHOLE) + referenceDate_PMILLIS;
+ timeStruct.end_PMILLIS = timeStruct.start_PMILLIS + daysToMillis(1);
}
- setTimeStructTextCenter( timeStructs, labelAlign, maxViewDuration_MILLIS );
+ timeStruct.viewStart_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMin_PMILLIS);
+ timeStruct.viewEnd_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMax_PMILLIS);
- return timeStructs;
- }
+ maxViewDuration_MILLIS = Math.max(maxViewDuration_MILLIS, timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS);
- function createTimeStructsAbsolute( timeAxis : TimeAxis1D, factory : TimeStructFactory, timeZone : string, tickTimes_PMILLIS : number[], labelAlign : number ) : TimeStruct[] {
- var dMin_PMILLIS = timeAxis.tMin_PMILLIS;
- var dMax_PMILLIS = timeAxis.tMax_PMILLIS;
+ timeStructs.push(timeStruct);
+ }
- var timeStructs : TimeStruct[] = [];
- var maxViewDuration_MILLIS = Number.NEGATIVE_INFINITY;
+ setTimeStructTextCenter(timeStructs, labelAlign, maxViewDuration_MILLIS);
- var previous_PMILLIS : number = null;
+ return timeStructs;
+}
- for ( var n = 0; n < tickTimes_PMILLIS.length; n++ ) {
- var tickTime_PMILLIS = tickTimes_PMILLIS[ n ];
- var timeStruct = factory( );
- var m = timeStruct.setTime( tickTime_PMILLIS, timeZone );
- var start_PMILLIS = m.valueOf( );
+function createTimeStructsAbsolute(timeAxis: TimeAxis1D, factory: TimeStructFactory, timeZone: string, tickTimes_PMILLIS: number[], labelAlign: number): TimeStruct[] {
+ const dMin_PMILLIS = timeAxis.tMin_PMILLIS;
+ const dMax_PMILLIS = timeAxis.tMax_PMILLIS;
- // XXX: Floating-point comparison can be unintuitive
- if ( hasval( previous_PMILLIS ) && start_PMILLIS === previous_PMILLIS ) continue;
- previous_PMILLIS = start_PMILLIS;
+ const timeStructs: TimeStruct[] = [];
+ let maxViewDuration_MILLIS = Number.NEGATIVE_INFINITY;
- timeStruct.start_PMILLIS = start_PMILLIS;
- timeStruct.incrementTime( m );
- timeStruct.end_PMILLIS = m.valueOf( );
- timeStruct.viewStart_PMILLIS = clamp( timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMin_PMILLIS );
- timeStruct.viewEnd_PMILLIS = clamp( timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMax_PMILLIS );
+ let previous_PMILLIS: number = null;
- maxViewDuration_MILLIS = Math.max( maxViewDuration_MILLIS, timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS );
+ for (let n = 0; n < tickTimes_PMILLIS.length; n++) {
+ const tickTime_PMILLIS = tickTimes_PMILLIS[n];
+ const timeStruct = factory();
+ const m = timeStruct.setTime(tickTime_PMILLIS, timeZone);
+ const start_PMILLIS = m.valueOf();
- timeStructs.push( timeStruct );
+ // XXX: Floating-point comparison can be unintuitive
+ if (hasval(previous_PMILLIS) && start_PMILLIS === previous_PMILLIS) {
+ continue;
}
+ previous_PMILLIS = start_PMILLIS;
+
+ timeStruct.start_PMILLIS = start_PMILLIS;
+ timeStruct.incrementTime(m);
+ timeStruct.end_PMILLIS = m.valueOf();
+ timeStruct.viewStart_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMin_PMILLIS);
+ timeStruct.viewEnd_PMILLIS = clamp(timeStruct.start_PMILLIS, timeStruct.end_PMILLIS, dMax_PMILLIS);
- setTimeStructTextCenter( timeStructs, labelAlign, maxViewDuration_MILLIS );
+ maxViewDuration_MILLIS = Math.max(maxViewDuration_MILLIS, timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS);
- return timeStructs;
+ timeStructs.push(timeStruct);
}
- function setTimeStructTextCenter( timeStructs : TimeStruct[], labelAlign : number, maxViewDuration_MILLIS : number ) {
- for ( var n = 0; n < timeStructs.length; n++ ) {
- var timeStruct = timeStructs[ n ];
- var duration_MILLIS = timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS;
- var midpoint_PMILLIS = timeStruct.viewStart_PMILLIS + labelAlign*duration_MILLIS;
- var edge_PMILLIS = ( timeStruct.viewStart_PMILLIS === timeStruct.start_PMILLIS ? timeStruct.viewEnd_PMILLIS : timeStruct.viewStart_PMILLIS );
- var edginess = 1 - clamp( 0, 1, duration_MILLIS / maxViewDuration_MILLIS );
- timeStruct.textCenter_PMILLIS = midpoint_PMILLIS + edginess*( edge_PMILLIS - midpoint_PMILLIS );
- }
+ setTimeStructTextCenter(timeStructs, labelAlign, maxViewDuration_MILLIS);
+
+ return timeStructs;
+}
+
+function setTimeStructTextCenter(timeStructs: TimeStruct[], labelAlign: number, maxViewDuration_MILLIS: number) {
+ for (let n = 0; n < timeStructs.length; n++) {
+ const timeStruct = timeStructs[n];
+ const duration_MILLIS = timeStruct.viewEnd_PMILLIS - timeStruct.viewStart_PMILLIS;
+ const midpoint_PMILLIS = timeStruct.viewStart_PMILLIS + labelAlign * duration_MILLIS;
+ const edge_PMILLIS = (timeStruct.viewStart_PMILLIS === timeStruct.start_PMILLIS ? timeStruct.viewEnd_PMILLIS : timeStruct.viewStart_PMILLIS);
+ const edginess = 1 - clamp(0, 1, duration_MILLIS / maxViewDuration_MILLIS);
+ timeStruct.textCenter_PMILLIS = midpoint_PMILLIS + edginess * (edge_PMILLIS - midpoint_PMILLIS);
}
+}
- export function getTickTimes_PMILLIS( timeAxis : TimeAxis1D, sizePixels : number, tickSpacing : number, timeZone : string, referenceDate_PMILLIS : number ) : number[] {
- if ( hasval( referenceDate_PMILLIS ) ) {
- return getTickTimesRelative_PMILLIS( timeAxis, sizePixels, tickSpacing, referenceDate_PMILLIS );
- }
- else
- {
- return getTickTimesAbsolute_PMILLIS( timeAxis, sizePixels, tickSpacing, timeZone );
- }
+export function getTickTimes_PMILLIS(timeAxis: TimeAxis1D, sizePixels: number, tickSpacing: number, timeZone: string, referenceDate_PMILLIS: number): number[] {
+ if (hasval(referenceDate_PMILLIS)) {
+ return getTickTimesRelative_PMILLIS(timeAxis, sizePixels, tickSpacing, referenceDate_PMILLIS);
+ }
+ else {
+ return getTickTimesAbsolute_PMILLIS(timeAxis, sizePixels, tickSpacing, timeZone);
}
+}
- function getTickTimesRelative_PMILLIS( timeAxis : TimeAxis1D, sizePixels : number, tickSpacing : number, referenceDate_PMILLIS : number ) : number[] {
-
- var dMin_PMILLIS = timeAxis.tMin_PMILLIS;
- var dMax_PMILLIS = timeAxis.tMax_PMILLIS;
- var approxTickInterval_MILLIS = tickSpacing * ( dMax_PMILLIS - dMin_PMILLIS ) / sizePixels;
+function getTickTimesRelative_PMILLIS(timeAxis: TimeAxis1D, sizePixels: number, tickSpacing: number, referenceDate_PMILLIS: number): number[] {
- if ( approxTickInterval_MILLIS < daysToMillis( 1 ) ) {
- return getHourTickTimesRelative_PMILLIS( dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, referenceDate_PMILLIS );
- }
- else {
- return getDayTickTimesRelative_PMILLIS( dMin_PMILLIS, dMax_PMILLIS, sizePixels, tickSpacing, referenceDate_PMILLIS );
- }
+ const dMin_PMILLIS = timeAxis.tMin_PMILLIS;
+ const dMax_PMILLIS = timeAxis.tMax_PMILLIS;
+ const approxTickInterval_MILLIS = tickSpacing * (dMax_PMILLIS - dMin_PMILLIS) / sizePixels;
+
+ if (approxTickInterval_MILLIS < daysToMillis(1)) {
+ return getHourTickTimesRelative_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, referenceDate_PMILLIS);
}
+ else {
+ return getDayTickTimesRelative_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, sizePixels, tickSpacing, referenceDate_PMILLIS);
+ }
+}
- function getHourTickTimesRelative_PMILLIS( dMin_PMILLIS : number, dMax_PMILLIS : number, approxTickInterval_MILLIS : number, referenceDate_PMILLIS : number ) : number[] {
- var tickTimes = getHourTickTimes_PMILLIS( dMin_PMILLIS - referenceDate_PMILLIS, dMax_PMILLIS - referenceDate_PMILLIS, approxTickInterval_MILLIS, 0 );
-
- for ( var n = 0; n < tickTimes.length; n++ ) {
- tickTimes[n] = tickTimes[n] + referenceDate_PMILLIS;
- }
+function getHourTickTimesRelative_PMILLIS(dMin_PMILLIS: number, dMax_PMILLIS: number, approxTickInterval_MILLIS: number, referenceDate_PMILLIS: number): number[] {
+ const tickTimes = getHourTickTimes_PMILLIS(dMin_PMILLIS - referenceDate_PMILLIS, dMax_PMILLIS - referenceDate_PMILLIS, approxTickInterval_MILLIS, 0);
- return tickTimes;
+ for (let n = 0; n < tickTimes.length; n++) {
+ tickTimes[n] = tickTimes[n] + referenceDate_PMILLIS;
}
- function getDayTickTimesRelative_PMILLIS( dMin_PMILLIS : number, dMax_PMILLIS : number, sizePixels : number, tickSpacing : number, referenceDate_PMILLIS : number ) : number[] {
- var axis = new Axis1D( millisToDays( dMin_PMILLIS - referenceDate_PMILLIS ), millisToDays( dMax_PMILLIS - referenceDate_PMILLIS ) );
- var approxNumTicks = sizePixels / tickSpacing;
- var tickInterval = getTickInterval( axis, approxNumTicks );
- var tickCount = getTickCount( axis, tickInterval );
- var tickPositions = new Float32Array( tickCount );
- getTickPositions( axis, tickInterval, tickCount, tickPositions );
- var tickTimes_PMILLIS = [ ];
- for ( var n = 0; n < tickCount; n++ ) {
- tickTimes_PMILLIS.push( daysToMillis( tickPositions[n] ) + referenceDate_PMILLIS );
- }
- return tickTimes_PMILLIS;
- }
+ return tickTimes;
+}
- function getTickTimesAbsolute_PMILLIS( timeAxis : TimeAxis1D, sizePixels : number, tickSpacing : number, timeZone : string ) : number[] {
- var dMin_PMILLIS = timeAxis.tMin_PMILLIS;
- var dMax_PMILLIS = timeAxis.tMax_PMILLIS;
+function getDayTickTimesRelative_PMILLIS(dMin_PMILLIS: number, dMax_PMILLIS: number, sizePixels: number, tickSpacing: number, referenceDate_PMILLIS: number): number[] {
+ const axis = new Axis1D(millisToDays(dMin_PMILLIS - referenceDate_PMILLIS), millisToDays(dMax_PMILLIS - referenceDate_PMILLIS));
+ const approxNumTicks = sizePixels / tickSpacing;
+ const tickInterval = getTickInterval(axis, approxNumTicks);
+ const tickCount = getTickCount(axis, tickInterval);
+ const tickPositions = new Float32Array(tickCount);
+ getTickPositions(axis, tickInterval, tickCount, tickPositions);
+ const tickTimes_PMILLIS = [];
+ for (let n = 0; n < tickCount; n++) {
+ tickTimes_PMILLIS.push(daysToMillis(tickPositions[n]) + referenceDate_PMILLIS);
+ }
+ return tickTimes_PMILLIS;
+}
- // NOTE: moment.js reports time zone offset reversed from Java Calendar, thus the negative sign
- var mMin = moment( dMin_PMILLIS ).zone( timeZone );
- var zoneOffset_MILLIS = -minutesToMillis( mMin.zone( ) );
+function getTickTimesAbsolute_PMILLIS(timeAxis: TimeAxis1D, sizePixels: number, tickSpacing: number, timeZone: string): number[] {
+ const dMin_PMILLIS = timeAxis.tMin_PMILLIS;
+ const dMax_PMILLIS = timeAxis.tMax_PMILLIS;
- var approxTickInterval_MILLIS = tickSpacing * ( dMax_PMILLIS - dMin_PMILLIS ) / sizePixels;
+ // NOTE: moment.js reports time zone offset reversed from Java Calendar, thus the negative sign
+ const mMin = moment(dMin_PMILLIS).zone(timeZone);
+ const zoneOffset_MILLIS = -minutesToMillis(mMin.zone());
- if ( approxTickInterval_MILLIS > daysToMillis( 60 ) ) {
- return getYearTickTimes_PMILLIS( dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone );
- }
- else if ( approxTickInterval_MILLIS > daysToMillis( 10 ) ) {
- return getMonthTickTimes_PMILLIS( dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone );
- }
- else if ( approxTickInterval_MILLIS > daysToMillis( 1 ) ) {
- return getDayTickTimes_PMILLIS( dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone );
- }
- else {
- return getHourTickTimes_PMILLIS( dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, zoneOffset_MILLIS );
- }
+ const approxTickInterval_MILLIS = tickSpacing * (dMax_PMILLIS - dMin_PMILLIS) / sizePixels;
+
+ if (approxTickInterval_MILLIS > daysToMillis(60)) {
+ return getYearTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone);
+ }
+ else if (approxTickInterval_MILLIS > daysToMillis(10)) {
+ return getMonthTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone);
+ }
+ else if (approxTickInterval_MILLIS > daysToMillis(1)) {
+ return getDayTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, timeZone);
+ }
+ else {
+ return getHourTickTimes_PMILLIS(dMin_PMILLIS, dMax_PMILLIS, approxTickInterval_MILLIS, zoneOffset_MILLIS);
}
+}
- function getYearTickTimes_PMILLIS( dMin_PMILLIS : number, dMax_PMILLIS : number, approxTickInterval_MILLIS : number, timeZone : string ) : number[] {
- var m = moment( dMin_PMILLIS ).zone( timeZone );
+function getYearTickTimes_PMILLIS(dMin_PMILLIS: number, dMax_PMILLIS: number, approxTickInterval_MILLIS: number, timeZone: string): number[] {
+ const m = moment(dMin_PMILLIS).zone(timeZone);
- var currentYear = m.year( );
- var daysPerYear = 365.25; // assume 365.25 days in every year as a heuristic
- var approxTickInterval_YEARS = millisToDays( approxTickInterval_MILLIS ) / daysPerYear;
+ const currentYear = m.year();
+ const daysPerYear = 365.25; // assume 365.25 days in every year as a heuristic
+ const approxTickInterval_YEARS = millisToDays(approxTickInterval_MILLIS) / daysPerYear;
- var yearOrderFactor = 6.0;
- var stepYears = getYearStep( approxTickInterval_YEARS * yearOrderFactor );
- var startYear = getRoundedYear( currentYear, stepYears );
+ const yearOrderFactor = 6.0;
+ const stepYears = getYearStep(approxTickInterval_YEARS * yearOrderFactor);
+ const startYear = getRoundedYear(currentYear, stepYears);
- m.year( startYear ).month( 0 ).date( 1 ).hours( 0 ).minutes( 0 ).seconds( 0 ).milliseconds( 0 );
+ m.year(startYear).month(0).date(1).hours(0).minutes(0).seconds(0).milliseconds(0);
- var tickTimes_PMILLIS = [ ];
- while ( m.valueOf( ) <= dMax_PMILLIS ) {
- tickTimes_PMILLIS.push( m.valueOf( ) );
- m.add( 'years', stepYears );
- }
- return tickTimes_PMILLIS;
+ const tickTimes_PMILLIS = [];
+ while (m.valueOf() <= dMax_PMILLIS) {
+ tickTimes_PMILLIS.push(m.valueOf());
+ m.add('years', stepYears);
}
+ return tickTimes_PMILLIS;
+}
- function getYearStep( spanYears : number ) : number {
- var log10 = Math.log( spanYears ) / Math.LN10;
- var order = Math.floor( log10 );
- if ( ( log10 - order ) > ( 1.0 - 1e-12 ) ) order++;
-
- return Math.max( 1, Math.pow( 10, order ) );
+function getYearStep(spanYears: number): number {
+ const log10Span = Math.log(spanYears) / Math.LN10;
+ let orderSpan = Math.floor(log10Span);
+ if ((log10Span - orderSpan) > (1.0 - 1e-12)) {
+ orderSpan++;
}
+ return Math.max(1, Math.pow(10, orderSpan));
+}
- function getRoundedYear( currentYear : number, yearStep : number ) : number {
- var numSteps = Math.floor( currentYear / yearStep );
- return numSteps * yearStep;
- }
+function getRoundedYear(currentYear: number, yearStep: number): number {
+ const numSteps = Math.floor(currentYear / yearStep);
+ return numSteps * yearStep;
+}
- function getMonthTickTimes_PMILLIS( dMin_PMILLIS : number, dMax_PMILLIS : number, approxTickInterval_MILLIS : number, timeZone : string ) : number[] {
- var m = moment( dMin_PMILLIS ).zone( timeZone ).date( 1 ).hours( 0 ).minutes( 0 ).seconds( 0 ).milliseconds( 0 );
- var tickTimes_PMILLIS = [ ];
- while ( m.valueOf( ) <= dMax_PMILLIS ) {
- tickTimes_PMILLIS.push( m.valueOf( ) );
- m.add( 'months', 1 );
- }
- return tickTimes_PMILLIS;
+
+function getMonthTickTimes_PMILLIS(dMin_PMILLIS: number, dMax_PMILLIS: number, approxTickInterval_MILLIS: number, timeZone: string): number[] {
+ const m = moment(dMin_PMILLIS).zone(timeZone).date(1).hours(0).minutes(0).seconds(0).milliseconds(0);
+ const tickTimes_PMILLIS = [];
+ while (m.valueOf() <= dMax_PMILLIS) {
+ tickTimes_PMILLIS.push(m.valueOf());
+ m.add('months', 1);
}
+ return tickTimes_PMILLIS;
+}
- function getDayTickTimes_PMILLIS( dMin_PMILLIS : number, dMax_PMILLIS : number, approxTickInterval_MILLIS : number, timeZone : string ) : number[] {
- var tickInterval_DAYS = calculateTickInterval_DAYS( approxTickInterval_MILLIS );
+function getDayTickTimes_PMILLIS(dMin_PMILLIS: number, dMax_PMILLIS: number, approxTickInterval_MILLIS: number, timeZone: string): number[] {
+ const tickInterval_DAYS = calculateTickInterval_DAYS(approxTickInterval_MILLIS);
- // initialize calendar off start time and reset fields less than month
- var m = moment( dMin_PMILLIS ).zone( timeZone ).date( 1 ).hours( 0 ).minutes( 0 ).seconds( 0 ).milliseconds( 0 );
+ // initialize calendar off start time and reset fields less than month
+ const m = moment(dMin_PMILLIS).zone(timeZone).date(1).hours(0).minutes(0).seconds(0).milliseconds(0);
- var endTime_PMILLIS = dMax_PMILLIS + daysToMillis( tickInterval_DAYS );
- var currentMonth = m.month( );
+ const endTime_PMILLIS = dMax_PMILLIS + daysToMillis(tickInterval_DAYS);
+ let currentMonth = m.month();
- var tickTimes_PMILLIS = [ ];
+ const tickTimes_PMILLIS = [];
- while ( m.valueOf( ) <= endTime_PMILLIS ) {
- // ensure ticks always fall on the first day of the month
- var newMonth = m.month( );
- if ( newMonth !== currentMonth ) {
- m.date( 1 );
- currentMonth = newMonth;
- }
-
- // prevent display of ticks too close to the end of the month
- var maxDom = m.clone( ).endOf( 'month' ).date( );
- var dom = m.date( );
- if ( maxDom - dom + 1 >= tickInterval_DAYS / 2 ) {
- tickTimes_PMILLIS.push( m.valueOf( ) );
- }
+ while (m.valueOf() <= endTime_PMILLIS) {
+ // ensure ticks always fall on the first day of the month
+ const newMonth = m.month();
+ if (newMonth !== currentMonth) {
+ m.date(1);
+ currentMonth = newMonth;
+ }
- m.add( 'days', tickInterval_DAYS );
+ // prevent display of ticks too close to the end of the month
+ const maxDom = m.clone().endOf('month').date();
+ const dom = m.date();
+ if (maxDom - dom + 1 >= tickInterval_DAYS / 2) {
+ tickTimes_PMILLIS.push(m.valueOf());
}
- return tickTimes_PMILLIS;
+ m.add('days', tickInterval_DAYS);
}
+ return tickTimes_PMILLIS;
+}
- function getHourTickTimes_PMILLIS( dMin_PMILLIS : number, dMax_PMILLIS : number, approxTickInterval_MILLIS : number, zoneOffset_MILLIS : number ) : number[] {
- var tickInterval_MILLIS = calculateTickInterval_MILLIS( approxTickInterval_MILLIS );
- var ticksSince1970 = Math.floor( ( dMin_PMILLIS + zoneOffset_MILLIS ) / tickInterval_MILLIS );
- var firstTick_PMILLIS = ( ticksSince1970 * tickInterval_MILLIS ) - zoneOffset_MILLIS;
- var numTicks = Math.ceil( 1 + ( dMax_PMILLIS - firstTick_PMILLIS ) / tickInterval_MILLIS );
+function getHourTickTimes_PMILLIS(dMin_PMILLIS: number, dMax_PMILLIS: number, approxTickInterval_MILLIS: number, zoneOffset_MILLIS: number): number[] {
+ const tickInterval_MILLIS = calculateTickInterval_MILLIS(approxTickInterval_MILLIS);
- var tickTimes_PMILLIS = [ ];
- for ( var n = 0; n < numTicks; n++ ) {
- tickTimes_PMILLIS.push( firstTick_PMILLIS + n*tickInterval_MILLIS );
- }
- return tickTimes_PMILLIS;
+ const ticksSince1970 = Math.floor((dMin_PMILLIS + zoneOffset_MILLIS) / tickInterval_MILLIS);
+ const firstTick_PMILLIS = (ticksSince1970 * tickInterval_MILLIS) - zoneOffset_MILLIS;
+ const numTicks = Math.ceil(1 + (dMax_PMILLIS - firstTick_PMILLIS) / tickInterval_MILLIS);
+
+ const tickTimes_PMILLIS = [];
+ for (let n = 0; n < numTicks; n++) {
+ tickTimes_PMILLIS.push(firstTick_PMILLIS + n * tickInterval_MILLIS);
}
+ return tickTimes_PMILLIS;
+}
- var tickIntervalRungs_DAYS = [ 2, 3, 4, 5, 8, 10 ];
+const tickIntervalRungs_DAYS = [2, 3, 4, 5, 8, 10];
- function calculateTickInterval_DAYS( approxTickInterval_MILLIS : number ) {
- var approxTickInterval_DAYS = millisToDays( approxTickInterval_MILLIS );
- for ( var n = 0 ; n < tickIntervalRungs_DAYS.length ; n++ ) {
- if ( approxTickInterval_DAYS <= tickIntervalRungs_DAYS[ n ] ) {
- return tickIntervalRungs_DAYS[ n ];
- }
+function calculateTickInterval_DAYS(approxTickInterval_MILLIS: number) {
+ const approxTickInterval_DAYS = millisToDays(approxTickInterval_MILLIS);
+ for (let n = 0; n < tickIntervalRungs_DAYS.length; n++) {
+ if (approxTickInterval_DAYS <= tickIntervalRungs_DAYS[n]) {
+ return tickIntervalRungs_DAYS[n];
}
- return 10;
- }
-
-
- var tickIntervalRungs_MILLIS = [ secondsToMillis( 1 ),
- secondsToMillis( 2 ),
- secondsToMillis( 5 ),
- secondsToMillis( 10 ),
- secondsToMillis( 15 ),
- secondsToMillis( 20 ),
- secondsToMillis( 30 ),
- minutesToMillis( 1 ),
- minutesToMillis( 2 ),
- minutesToMillis( 5 ),
- minutesToMillis( 10 ),
- minutesToMillis( 15 ),
- minutesToMillis( 20 ),
- minutesToMillis( 30 ),
- hoursToMillis( 1 ),
- hoursToMillis( 2 ),
- hoursToMillis( 3 ),
- hoursToMillis( 6 ),
- hoursToMillis( 12 ),
- daysToMillis( 1 ) ];
-
-
- function calculateTickInterval_MILLIS( approxTickInterval_MILLIS : number ) : number {
- for ( var n = 0; n < tickIntervalRungs_MILLIS.length; n++ ) {
- if ( approxTickInterval_MILLIS <= tickIntervalRungs_MILLIS[ n ] ) {
- return tickIntervalRungs_MILLIS[ n ];
- }
- }
- return daysToMillis( 1 );
}
+ return 10;
+}
- function getTickInterval_MILLIS( tickTimes_PMILLIS : number[] ) : number {
- if ( hasval( tickTimes_PMILLIS ) && tickTimes_PMILLIS.length > 1 ) {
- return ( tickTimes_PMILLIS[ 1 ] - tickTimes_PMILLIS[ 0 ] );
- }
- else {
- return secondsToMillis( 1 );
- }
- }
+const tickIntervalRungs_MILLIS = [secondsToMillis(1),
+secondsToMillis(2),
+secondsToMillis(5),
+secondsToMillis(10),
+secondsToMillis(15),
+secondsToMillis(20),
+secondsToMillis(30),
+minutesToMillis(1),
+minutesToMillis(2),
+minutesToMillis(5),
+minutesToMillis(10),
+minutesToMillis(15),
+minutesToMillis(20),
+minutesToMillis(30),
+hoursToMillis(1),
+hoursToMillis(2),
+hoursToMillis(3),
+hoursToMillis(6),
+hoursToMillis(12),
+daysToMillis(1)];
+
+
+function calculateTickInterval_MILLIS(approxTickInterval_MILLIS: number): number {
+ for (let n = 0; n < tickIntervalRungs_MILLIS.length; n++) {
+ if (approxTickInterval_MILLIS <= tickIntervalRungs_MILLIS[n]) {
+ return tickIntervalRungs_MILLIS[n];
+ }
+ }
+ return daysToMillis(1);
+}
+function getTickInterval_MILLIS(tickTimes_PMILLIS: number[]): number {
+ if (hasval(tickTimes_PMILLIS) && tickTimes_PMILLIS.length > 1) {
+ return (tickTimes_PMILLIS[1] - tickTimes_PMILLIS[0]);
+ }
+ else {
+ return secondsToMillis(1);
+ }
}
+
+
+
diff --git a/src/webglimpse/timeline/time_grid_painter.ts b/src/webglimpse/timeline/time_grid_painter.ts
index 83a85a8..b7795cf 100644
--- a/src/webglimpse/timeline/time_grid_painter.ts
+++ b/src/webglimpse/timeline/time_grid_painter.ts
@@ -27,65 +27,72 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Color, black } from '../color';
+import { TimeAxis1D } from './time_axis';
+import { Painter } from '../core';
+import { Program, UniformColor, Attribute } from '../shader';
+import { xyFrac_VERTSHADER, solid_FRAGSHADER } from '../misc';
+import { newDynamicBuffer } from '../buffer';
+import { BoundsUnmodifiable } from '../bounds';
+import { ensureCapacityFloat32, GL, hasval, parseTime_PMILLIS } from '../util/util';
+import { getTickTimes_PMILLIS } from './time_axis_painter';
- export interface TimeGridPainterOptions {
- tickSpacing? : number;
- gridColor? : Color;
- referenceDate? : string;
- }
+export interface TimeGridPainterOptions {
+ tickSpacing?: number;
+ gridColor?: Color;
+ referenceDate?: string;
+}
- export function newTimeGridPainter( timeAxis : TimeAxis1D, isVerticalAxis : boolean, timeZone : string, options? : TimeGridPainterOptions ) : Painter {
- var tickSpacing = ( hasval( options ) && hasval( options.tickSpacing ) ? options.tickSpacing : 60 );
- var gridColor = ( hasval( options ) && hasval( options.gridColor ) ? options.gridColor : black );
- var referenceDate_PMILLIS = ( hasval( options ) && hasval( options.referenceDate ) ? parseTime_PMILLIS( options.referenceDate ) : undefined );
+export function newTimeGridPainter(timeAxis: TimeAxis1D, isVerticalAxis: boolean, timeZone: string, options?: TimeGridPainterOptions): Painter {
+ const tickSpacing = (hasval(options) && hasval(options.tickSpacing) ? options.tickSpacing : 60);
+ const gridColor = (hasval(options) && hasval(options.gridColor) ? options.gridColor : black);
+ const referenceDate_PMILLIS = (hasval(options) && hasval(options.referenceDate) ? parseTime_PMILLIS(options.referenceDate) : undefined);
- var program = new Program( xyFrac_VERTSHADER, solid_FRAGSHADER );
- var u_Color = new UniformColor( program, 'u_Color' );
- var a_XyFrac = new Attribute( program, 'a_XyFrac' );
+ const program = new Program(xyFrac_VERTSHADER, solid_FRAGSHADER);
+ const u_Color = new UniformColor(program, 'u_Color');
+ const a_XyFrac = new Attribute(program, 'a_XyFrac');
- var xyFrac = new Float32Array( 0 );
- var xyFracBuffer = newDynamicBuffer( );
+ let xyFrac = new Float32Array(0);
+ const xyFracBuffer = newDynamicBuffer();
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- var tickTimes_PMILLIS = getTickTimes_PMILLIS( timeAxis, ( isVerticalAxis ? viewport.h : viewport.w ), tickSpacing, timeZone, referenceDate_PMILLIS );
- var tickCount = tickTimes_PMILLIS.length;
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ const tickTimes_PMILLIS = getTickTimes_PMILLIS(timeAxis, (isVerticalAxis ? viewport.h : viewport.w), tickSpacing, timeZone, referenceDate_PMILLIS);
+ const tickCount = tickTimes_PMILLIS.length;
- program.use( gl );
- u_Color.setData( gl, gridColor );
+ program.use(gl);
+ u_Color.setData(gl, gridColor);
- xyFrac = ensureCapacityFloat32( xyFrac, 4*tickCount );
- for ( var n = 0; n < tickCount; n++ ) {
- var tFrac = timeAxis.tFrac( tickTimes_PMILLIS[ n ] );
- if ( isVerticalAxis ) {
- tFrac = ( Math.floor( tFrac*viewport.h ) + 0.5 ) / viewport.h;
- xyFrac[ ( 4*n + 0 ) ] = 0;
- xyFrac[ ( 4*n + 1 ) ] = tFrac;
- xyFrac[ ( 4*n + 2 ) ] = 1;
- xyFrac[ ( 4*n + 3 ) ] = tFrac;
- }
- else {
- // Adding epsilon is a crude way to compensate for floating-point error (which is probably introduced up where we compute tFrac)
- tFrac = ( Math.floor( tFrac*viewport.w + 1e-4 ) + 0.5 ) / viewport.w;
- xyFrac[ ( 4*n + 0 ) ] = tFrac;
- xyFrac[ ( 4*n + 1 ) ] = 0;
- xyFrac[ ( 4*n + 2 ) ] = tFrac;
- xyFrac[ ( 4*n + 3 ) ] = 1;
- }
+ xyFrac = ensureCapacityFloat32(xyFrac, 4 * tickCount);
+ for (let n = 0; n < tickCount; n++) {
+ let tFrac = timeAxis.tFrac(tickTimes_PMILLIS[n]);
+ if (isVerticalAxis) {
+ tFrac = (Math.floor(tFrac * viewport.h) + 0.5) / viewport.h;
+ xyFrac[(4 * n + 0)] = 0;
+ xyFrac[(4 * n + 1)] = tFrac;
+ xyFrac[(4 * n + 2)] = 1;
+ xyFrac[(4 * n + 3)] = tFrac;
}
- xyFracBuffer.setData( xyFrac.subarray( 0, 4*tickCount ) );
- a_XyFrac.setDataAndEnable( gl, xyFracBuffer, 2, GL.FLOAT );
+ else {
+ // Adding epsilon is a crude way to compensate for floating-point error (which is probably introduced up where we compute tFrac)
+ tFrac = (Math.floor(tFrac * viewport.w + 1e-4) + 0.5) / viewport.w;
+ xyFrac[(4 * n + 0)] = tFrac;
+ xyFrac[(4 * n + 1)] = 0;
+ xyFrac[(4 * n + 2)] = tFrac;
+ xyFrac[(4 * n + 3)] = 1;
+ }
+ }
+ xyFracBuffer.setData(xyFrac.subarray(0, 4 * tickCount));
+ a_XyFrac.setDataAndEnable(gl, xyFracBuffer, 2, GL.FLOAT);
- // IE does not support lineWidths other than 1, so make sure all browsers use lineWidth of 1
- gl.lineWidth( 1 );
- gl.drawArrays( GL.LINES, 0, 2*tickCount );
+ // IE does not support lineWidths other than 1, so make sure all browsers use lineWidth of 1
+ gl.lineWidth(1);
+ gl.drawArrays(GL.LINES, 0, 2 * tickCount);
- a_XyFrac.disable( gl );
- program.endUse( gl );
- }
- }
+ a_XyFrac.disable(gl);
+ program.endUse(gl);
+ };
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/timeline/timeline_annotation_painter.ts b/src/webglimpse/timeline/timeline_annotation_painter.ts
index 9adfccc..3bb1cae 100644
--- a/src/webglimpse/timeline/timeline_annotation_painter.ts
+++ b/src/webglimpse/timeline/timeline_annotation_painter.ts
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 2014, Metron, Inc.
* All rights reserved.
@@ -28,119 +27,146 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
- export function newTimeseriesAnnotationPainterFactory( ) : TimelineTimeseriesPainterFactory {
- // Painter Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, dataAxis : Axis1D, model : TimelineModel, rowModel : TimelineRowModel, ui : TimelineUi ) : Painter {
-
- var textTextures = newTextTextureCache3( );
- var textureRenderer = new TextureRenderer( );
-
- var program = new Program( xyFrac_VERTSHADER, solid_FRAGSHADER );
- var u_Color = new UniformColor( program, 'u_Color' );
- var a_Position = new Attribute( program, 'a_XyFrac' );
-
- var xys = new Float32Array( 0 );
- xys = ensureCapacityFloat32( xys, 4 );
- var xysBuffer = newDynamicBuffer( );
-
- // Painter
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
-
- textTextures.resetTouches( );
-
- for ( var i = 0 ; i < rowModel.annotationGuids.length ; i++ ) {
- var annotationGuid : string = rowModel.annotationGuids.valueAt(i);
- var annotation : TimelineAnnotationModel = model.annotation( annotationGuid );
- var annotationStyle : TimelineAnnotationStyleUi = ui.annotationStyle( annotation.styleGuid );
-
- var font = hasval( annotationStyle.font ) ? annotationStyle.font : '11px verdana,sans-serif';
- var color = hasval( annotationStyle.color ) ? annotationStyle.color : white;
-
- var hTextOffset = hasval( annotationStyle.hTextOffset ) ? annotationStyle.hTextOffset : 0;
- var vTextOffset = hasval( annotationStyle.vTextOffset ) ? annotationStyle.vTextOffset : 0;
-
- // draw line
- if ( annotationStyle.uiHint === 'horizontal-line' || annotationStyle.uiHint === 'vertical-line' ) {
-
- if ( annotationStyle.uiHint === 'horizontal-line' ) {
- var xFrac = hasval( annotationStyle.align ) ? annotationStyle.align : 1 ;
- var yFrac = dataAxis.vFrac( annotation.y );
-
- xys[0] = 0;
- xys[1] = yFrac;
- xys[2] = 1;
- xys[3] = yFrac;
- }
- else if ( annotationStyle.uiHint === 'vertical-line' ) {
- var xFrac = timeAxis.tFrac( annotation.time_PMILLIS );
- var yFrac = hasval( annotationStyle.align ) ? annotationStyle.align : 1 ;
-
- xys[0] = xFrac;
- xys[1] = 0;
- xys[2] = xFrac;
- xys[3] = 1;
- }
-
- program.use( gl );
-
- u_Color.setData( gl, color );
-
- xysBuffer.setData( xys.subarray( 0, 4 ) );
- a_Position.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
-
- gl.drawArrays( GL.LINES, 0, 2 );
-
- program.endUse( gl );
- }
- else {
- var xFrac = timeAxis.tFrac( annotation.time_PMILLIS );
- var yFrac = dataAxis.vFrac( annotation.y );
+import { TimelineTimeseriesPainterFactory } from './timeline_timeseries_row';
+import { Drawable, Painter } from '../core';
+import { TimeAxis1D } from './time_axis';
+import { Axis1D } from '../plot/axis';
+import { TimelineModel, TimelineRowModel, TimelineAnnotationModel } from './timeline_model';
+import { TimelineUi } from './timeline_ui';
+import { newTextTextureCache6 } from '../text';
+import { TextureRenderer } from '../texture';
+import { Program, UniformColor, Attribute } from '../shader';
+import { xyFrac_VERTSHADER, solid_FRAGSHADER } from '../misc';
+import { ensureCapacityFloat32, GL, hasval } from '../util/util';
+import { newDynamicBuffer } from '../buffer';
+import { BoundsUnmodifiable } from '../bounds';
+import { TimelineAnnotationStyleUi, TimelineAnnotationIcon } from './timeline_annotation_style';
+import { white } from '../color';
+
+export function newTimeseriesAnnotationPainterFactory(): TimelineTimeseriesPainterFactory {
+ // Painter Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, dataAxis: Axis1D, model: TimelineModel, rowModel: TimelineRowModel, ui: TimelineUi): Painter {
+
+ const textTextures = newTextTextureCache6();
+ const textureRenderer = new TextureRenderer();
+
+ const program = new Program(xyFrac_VERTSHADER, solid_FRAGSHADER);
+ const u_Color = new UniformColor(program, 'u_Color');
+ const a_Position = new Attribute(program, 'a_XyFrac');
+
+ let xys = new Float32Array(0);
+ xys = ensureCapacityFloat32(xys, 4);
+ const xysBuffer = newDynamicBuffer();
+
+ // Painter
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ textTextures.resetTouches();
+
+ let xFrac = 0;
+ let yFrac = 0;
+
+ for (let i = 0; i < rowModel.annotationGuids.length; i++) {
+ const annotationGuid: string = rowModel.annotationGuids.valueAt(i);
+ const annotation: TimelineAnnotationModel = model.annotation(annotationGuid);
+ const annotationStyle: TimelineAnnotationStyleUi = ui.annotationStyle(annotation.styleGuid);
+
+ const font = hasval(annotationStyle.font) ? annotationStyle.font : '11px verdana,sans-serif';
+ const color = hasval(annotationStyle.color) ? annotationStyle.color : white;
+ const bgColorString = hasval(annotationStyle.bgColor) ? annotationStyle.bgColor.rgbaString : '#ffffff00';
+ const bgPadding = hasval(annotationStyle.bgPadding) ? annotationStyle.bgPadding : 0;
+ const bgBorderRadius = hasval(annotationStyle.bgBorderRadius) ? annotationStyle.bgBorderRadius : 0;
+
+ const hTextOffset = hasval(annotationStyle.hTextOffset) ? annotationStyle.hTextOffset : 0;
+ const vTextOffset = hasval(annotationStyle.vTextOffset) ? annotationStyle.vTextOffset : 0;
+
+ // draw line
+ if (annotationStyle.uiHint === 'horizontal-line' || annotationStyle.uiHint === 'vertical-line') {
+
+ if (annotationStyle.uiHint === 'horizontal-line') {
+ xFrac = hasval(annotationStyle.align) ? annotationStyle.align : 1;
+ yFrac = dataAxis.vFrac(annotation.y);
+
+ xys[0] = 0;
+ xys[1] = yFrac;
+ xys[2] = 1;
+ xys[3] = yFrac;
}
-
- textureRenderer.begin( gl, viewport );
-
- // draw icons
- for ( var n = 0; n < annotationStyle.numIcons; n++ ) {
- var icon : TimelineAnnotationIcon = annotationStyle.icon( n );
-
- var xFracOffset = xFrac + ( hasval( icon.hOffset ) ? icon.hOffset : 0 ) / viewport.w;
- var yFracOffset = yFrac + ( hasval( icon.vOffset ) ? icon.vOffset : 0 ) / viewport.h;
-
- var iconTexture = ui.loadImage( icon.url, function( ) { drawable.redraw( ); } );
- if ( iconTexture ) {
- var options = { xAnchor: ( hasval( icon.hAlign ) ? icon.hAlign : 0.5 ),
- yAnchor: ( hasval( icon.hAlign ) ? icon.hAlign : 0.5 ),
- width: icon.displayWidth,
- height: icon.displayHeight,
- rotation_CCWRAD: 0 };
-
- textureRenderer.draw( gl, iconTexture, xFracOffset, yFracOffset, options );
- }
+ else if (annotationStyle.uiHint === 'vertical-line') {
+ xFrac = timeAxis.tFrac(annotation.time_PMILLIS);
+ yFrac = hasval(annotationStyle.align) ? annotationStyle.align : 1;
+
+ xys[0] = xFrac;
+ xys[1] = 0;
+ xys[2] = xFrac;
+ xys[3] = 1;
}
-
- // draw text label
- if ( hasval( annotation.label ) ) {
-
- var textTexture = textTextures.value( font, color.rgbaString, annotation.label );
-
- var xFracOffset = xFrac + hTextOffset / viewport.w;
- var yFracOffset = yFrac + vTextOffset / viewport.h;
-
- var xAnchor = hasval( annotationStyle.hTextAlign ) ? annotationStyle.hTextAlign : 0;
- var yAnchor = textTexture.yAnchor( hasval( annotationStyle.vTextAlign ) ? annotationStyle.vTextAlign : 0.5 );
-
- textureRenderer.draw( gl, textTexture, xFracOffset, yFracOffset, { xAnchor: xAnchor, yAnchor: yAnchor } );
+
+ program.use(gl);
+
+ u_Color.setData(gl, color);
+
+ xysBuffer.setData(xys.subarray(0, 4));
+ a_Position.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+
+ gl.drawArrays(GL.LINES, 0, 2);
+
+ program.endUse(gl);
+ }
+ else {
+ xFrac = timeAxis.tFrac(annotation.time_PMILLIS);
+ yFrac = dataAxis.vFrac(annotation.y);
+ }
+
+ textureRenderer.begin(gl, viewport);
+
+ // draw icons
+ for (let n = 0; n < annotationStyle.numIcons; n++) {
+ const icon: TimelineAnnotationIcon = annotationStyle.icon(n);
+
+ const xFracOffset = xFrac + (hasval(icon.hOffset) ? icon.hOffset : 0) / viewport.w;
+ const yFracOffset = yFrac + (hasval(icon.vOffset) ? icon.vOffset : 0) / viewport.h;
+
+ const iconTexture = ui.loadImage(icon.url, function () { drawable.redraw(); });
+ if (iconTexture) {
+ const options = {
+ xAnchor: (hasval(icon.hAlign) ? icon.hAlign : 0.5),
+ yAnchor: (hasval(icon.hAlign) ? icon.hAlign : 0.5),
+ width: icon.displayWidth,
+ height: icon.displayHeight,
+ rotation_CCWRAD: 0
+ };
+
+ textureRenderer.draw(gl, iconTexture, xFracOffset, yFracOffset, options);
}
-
- textureRenderer.end( gl );
}
-
- textTextures.retainTouched( );
- };
+
+ // draw text label
+ if (hasval(annotation.label)) {
+ const textTexture = textTextures.value(
+ font,
+ color.rgbaString,
+ annotation.label,
+ bgColorString,
+ bgPadding.toString(),
+ bgBorderRadius.toString()
+ );
+
+ const xFracOffset = xFrac + hTextOffset / viewport.w;
+ const yFracOffset = yFrac + vTextOffset / viewport.h;
+
+ const xAnchor = hasval(annotationStyle.hTextAlign) ? annotationStyle.hTextAlign : 0;
+ const yAnchor = textTexture.yAnchor(hasval(annotationStyle.vTextAlign) ? annotationStyle.vTextAlign : 0.5);
+
+ textureRenderer.draw(gl, textTexture, xFracOffset, yFracOffset, { xAnchor: xAnchor, yAnchor: yAnchor });
+ }
+
+ textureRenderer.end(gl);
+ }
+
+ textTextures.retainTouched();
};
- }
-}
\ No newline at end of file
+ };
+}
diff --git a/src/webglimpse/timeline/timeline_annotation_style.ts b/src/webglimpse/timeline/timeline_annotation_style.ts
index 6722d63..6d2cf8d 100644
--- a/src/webglimpse/timeline/timeline_annotation_style.ts
+++ b/src/webglimpse/timeline/timeline_annotation_style.ts
@@ -27,165 +27,188 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
- export interface TimelineAnnotationIcon {
- url : string;
- displayWidth : number; // horizontal size of icon in pixels
- displayHeight : number; // vertical size of icon in pixels
- hAlign : number; // relative location of center pixel of icon (0=left side, 1=right side)
- vAlign : number; // relative location of center pixel of icon (0=left side, 1=right side)
- hOffset : number; // center pixel horizontal offset in pixels
- vOffset : number; // center pixel vertical offset in pixels
- }
-
- export interface TimelineAnnotationStyle {
- styleGuid : string;
- color? : string;
- font? : string; // label font
- hTextAlign? : number; // relative location of center pixel of text (0=left side, 1=right side)
- vTextAlign? : number; // relative location of center pixel of text (0=left side, 1=right side)
- hTextOffset? : number; // horizontal offset of the label text in pixels
- vTextOffset? : number; // vertical offset of the label text in pixels
- align? : number; // for horizontal-line, vertical-line only, relative location of text/icons along
- // annotation line (0 = bottom/left, 1=top/right)
- uiHint? : string; // one of: point, horizontal-line, vertical-line
- icons? : TimelineAnnotationIcon[];
- }
-
- export class TimelineAnnotationIconUi {
- private _url : string;
- private _displayWidth : number;
- private _displayHeight : number;
- private _hAlign : number;
- private _vAlign : number;
- private _hOffset : number;
- private _vOffset : number;
-
- constructor( icon : TimelineAnnotationIcon ) {
- this._setAttrs( icon );
- }
-
- private _setAttrs( icon : TimelineAnnotationIcon ) {
- this._url = icon.url;
- this._displayWidth = icon.displayWidth;
- this._displayHeight = icon.displayHeight;
- this._hAlign = icon.hAlign;
- this._vAlign = icon.vAlign;
- this._hOffset = icon.hOffset;
- this._vOffset = icon.vOffset;
- }
-
- get url( ) : string { return this._url; }
- get displayWidth( ) : number { return this._displayWidth; }
- get displayHeight( ) : number { return this._displayHeight; }
- get hAlign( ) : number { return this._hAlign; }
- get vAlign( ) : number { return this._vAlign; }
- get hOffset( ) : number { return this._hOffset; }
- get vOffset( ) : number { return this._vOffset; }
-
- snapshot( ) : TimelineAnnotationIcon {
- return {
- url: this._url,
- displayWidth: this._displayWidth,
- displayHeight: this._displayHeight,
- hAlign: this._hAlign,
- vAlign: this._vAlign,
- hOffset: this._hOffset,
- vOffset: this._vOffset
- };
- }
- }
-
-
-
- export class TimelineAnnotationStyleUi {
- private _styleGuid : string;
- private _color : Color;
- private _font : string;
- private _vTextOffset : number;
- private _hTextOffset : number;
- private _vTextAlign : number;
- private _hTextAlign : number;
- private _align : number;
- private _uiHint : string;
- private _icons : TimelineAnnotationIconUi[];
-
- constructor( style : TimelineAnnotationStyle ) {
- this._styleGuid = style.styleGuid;
- this._setAttrs( style );
- }
-
- get styleGuid( ) : string {
- return this._styleGuid;
- }
-
- private _setAttrs( style : TimelineAnnotationStyle ) {
- this._color = hasval( style.color ) ? parseCssColor( style.color ) : undefined;
- this._font = style.font ;
- this._hTextOffset = style.hTextOffset;
- this._vTextOffset = style.vTextOffset;
- this._hTextAlign = style.hTextAlign;
- this._vTextAlign = style.vTextAlign;
- this._align = style.align;
- this._uiHint = style.uiHint;
- this._icons = hasval( style.color ) ? style.icons.map( ( icon )=>{ return new TimelineAnnotationIconUi( icon ); } ) : [];
- }
-
- get numIcons( ) : number {
- return this._icons.length;
- }
-
- icon( index : number ) : TimelineAnnotationIconUi {
- return this._icons[ index ];
- }
-
- get color( ) : Color {
- return this._color;
- }
-
- get font( ) : string {
- return this._font;
- }
-
- get hTextOffset( ) : number {
- return this._hTextOffset;
- }
-
- get vTextOffset( ) : number {
- return this._vTextOffset;
- }
-
- get hTextAlign( ) : number {
- return this._hTextAlign;
- }
-
- get vTextAlign( ) : number {
- return this._vTextAlign;
- }
-
- get align( ) : number {
- return this._align;
- }
-
- get uiHint( ) : string {
- return this._uiHint;
- }
-
- snapshot( ) : TimelineAnnotationStyle {
- return {
- styleGuid: this._styleGuid,
- color: this._color.cssString,
- font: this._font,
- vTextOffset: this._hTextOffset,
- hTextOffset: this._vTextOffset,
- vTextAlign: this._hTextAlign,
- hTextAlign: this._vTextAlign,
- align: this._align,
- uiHint: this._uiHint,
- icons: this._icons.map( (ui)=>ui.snapshot() )
- };
- }
- }
-
-}
\ No newline at end of file
+import { Color, parseCssColor } from '../color';
+import { hasval } from '../util/util';
+
+export interface TimelineAnnotationIcon {
+ url: string;
+ displayWidth: number; // horizontal size of icon in pixels
+ displayHeight: number; // vertical size of icon in pixels
+ hAlign: number; // relative location of center pixel of icon (0=left side, 1=right side)
+ vAlign: number; // relative location of center pixel of icon (0=left side, 1=right side)
+ hOffset: number; // center pixel horizontal offset in pixels
+ vOffset: number; // center pixel vertical offset in pixels
+}
+
+export interface TimelineAnnotationStyle {
+ styleGuid: string;
+ color?: string;
+ font?: string; // label font
+ hTextAlign?: number; // relative location of center pixel of text (0=left side, 1=right side)
+ vTextAlign?: number; // relative location of center pixel of text (0=left side, 1=right side)
+ hTextOffset?: number; // horizontal offset of the label text in pixels
+ vTextOffset?: number; // vertical offset of the label text in pixels
+ align?: number; // for horizontal-line, vertical-line only, relative location of text/icons along
+ // annotation line (0 = bottom/left, 1=top/right)
+ uiHint?: string; // one of: point, horizontal-line, vertical-line
+ icons?: TimelineAnnotationIcon[];
+ bgColor?: string;
+ bgPadding?: number;
+ bgBorderRadius?: number;
+}
+
+export class TimelineAnnotationIconUi {
+ private _url: string;
+ private _displayWidth: number;
+ private _displayHeight: number;
+ private _hAlign: number;
+ private _vAlign: number;
+ private _hOffset: number;
+ private _vOffset: number;
+
+ constructor(icon: TimelineAnnotationIcon) {
+ this._setAttrs(icon);
+ }
+
+ private _setAttrs(icon: TimelineAnnotationIcon) {
+ this._url = icon.url;
+ this._displayWidth = icon.displayWidth;
+ this._displayHeight = icon.displayHeight;
+ this._hAlign = icon.hAlign;
+ this._vAlign = icon.vAlign;
+ this._hOffset = icon.hOffset;
+ this._vOffset = icon.vOffset;
+ }
+
+ get url(): string { return this._url; }
+ get displayWidth(): number { return this._displayWidth; }
+ get displayHeight(): number { return this._displayHeight; }
+ get hAlign(): number { return this._hAlign; }
+ get vAlign(): number { return this._vAlign; }
+ get hOffset(): number { return this._hOffset; }
+ get vOffset(): number { return this._vOffset; }
+
+ snapshot(): TimelineAnnotationIcon {
+ return {
+ url: this._url,
+ displayWidth: this._displayWidth,
+ displayHeight: this._displayHeight,
+ hAlign: this._hAlign,
+ vAlign: this._vAlign,
+ hOffset: this._hOffset,
+ vOffset: this._vOffset
+ };
+ }
+}
+
+
+
+export class TimelineAnnotationStyleUi {
+ private _styleGuid: string;
+ private _color: Color;
+ private _font: string;
+ private _vTextOffset: number;
+ private _hTextOffset: number;
+ private _vTextAlign: number;
+ private _hTextAlign: number;
+ private _align: number;
+ private _uiHint: string;
+ private _icons: TimelineAnnotationIconUi[];
+ private _bgColor?: Color;
+ private _bgPadding?: number;
+ private _bgBorderRadius?: number;
+
+ constructor(style: TimelineAnnotationStyle) {
+ this._styleGuid = style.styleGuid;
+ this._setAttrs(style);
+ }
+
+ get styleGuid(): string {
+ return this._styleGuid;
+ }
+
+ private _setAttrs(style: TimelineAnnotationStyle) {
+ this._color = hasval(style.color) ? parseCssColor(style.color) : undefined;
+ this._font = style.font;
+ this._hTextOffset = style.hTextOffset;
+ this._vTextOffset = style.vTextOffset;
+ this._hTextAlign = style.hTextAlign;
+ this._vTextAlign = style.vTextAlign;
+ this._align = style.align;
+ this._uiHint = style.uiHint;
+ this._icons = hasval(style.color) ? style.icons.map((icon) => new TimelineAnnotationIconUi(icon)) : [];
+ this._bgColor = hasval(style.bgColor) ? parseCssColor(style.bgColor) : undefined;
+ this._bgPadding = style.bgPadding;
+ this._bgBorderRadius = style.bgBorderRadius;
+ }
+
+ get numIcons(): number {
+ return this._icons.length;
+ }
+
+ icon(index: number): TimelineAnnotationIconUi {
+ return this._icons[index];
+ }
+
+ get color(): Color {
+ return this._color;
+ }
+
+ get font(): string {
+ return this._font;
+ }
+
+ get hTextOffset(): number {
+ return this._hTextOffset;
+ }
+
+ get vTextOffset(): number {
+ return this._vTextOffset;
+ }
+
+ get hTextAlign(): number {
+ return this._hTextAlign;
+ }
+
+ get vTextAlign(): number {
+ return this._vTextAlign;
+ }
+
+ get align(): number {
+ return this._align;
+ }
+
+ get uiHint(): string {
+ return this._uiHint;
+ }
+
+ get bgColor(): Color {
+ return this._bgColor;
+ }
+
+ get bgPadding(): number {
+ return this._bgPadding;
+ }
+ get bgBorderRadius(): number {
+ return this._bgBorderRadius;
+ }
+
+ snapshot(): TimelineAnnotationStyle {
+ return {
+ styleGuid: this._styleGuid,
+ color: this._color.cssString,
+ font: this._font,
+ vTextOffset: this._hTextOffset,
+ hTextOffset: this._vTextOffset,
+ vTextAlign: this._hTextAlign,
+ hTextAlign: this._vTextAlign,
+ align: this._align,
+ uiHint: this._uiHint,
+ icons: this._icons.map((ui) => ui.snapshot()),
+ bgColor: this._bgColor.cssString,
+ bgPadding: this._bgPadding,
+ bgBorderRadius: this._bgBorderRadius
+ };
+ }
+}
+
diff --git a/src/webglimpse/timeline/timeline_cursor_painter.ts b/src/webglimpse/timeline/timeline_cursor_painter.ts
index 4e8f899..593d150 100644
--- a/src/webglimpse/timeline/timeline_cursor_painter.ts
+++ b/src/webglimpse/timeline/timeline_cursor_painter.ts
@@ -27,208 +27,224 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
- export interface TimeseriesCursorPainterOptions {
- font? : string;
- textColor? : Color;
- // number of pixels between box and label text
- buffer_px? : number;
- // number of significant digits in text labels
- textDecimals? : number;
-
- // color of selection box and crosshair lines
- lineColor? : Color;
-
- // thickness of cursor crosshair lines in pixels
- crosshairThickness_px? : number;
-
- // width/height of selection box (shown at intersection beween crosshairs and timeseries) in pixels
- boxSize_px? : number;
- // thickness of selection box lines in pixels
- boxThickness_px? : number;
- }
-
- export function newTimeseriesCursorPainterFactory( cursorOptions? : TimeseriesCursorPainterOptions ) : TimelineTimeseriesPainterFactory {
-
- // Painter Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, dataAxis : Axis1D, model : TimelineModel, rowModel : TimelineRowModel, ui : TimelineUi, options : TimelineTimeseriesPainterOptions ) : Painter {
-
- var textColor = ( hasval( cursorOptions ) && hasval( cursorOptions.textColor ) ? cursorOptions.textColor : white );
- var lineColor = ( hasval( cursorOptions ) && hasval( cursorOptions.lineColor ) ? cursorOptions.lineColor : white );
- var font = ( hasval( cursorOptions ) && hasval( cursorOptions.font ) ? cursorOptions.font : options.timelineFont );
- var buffer_px = ( hasval( cursorOptions ) && hasval( cursorOptions.buffer_px ) ? cursorOptions.buffer_px : 4 );
- var textDecimals = ( hasval( cursorOptions ) && hasval( cursorOptions.textDecimals ) ? cursorOptions.textDecimals : 2 );
- var boxSize_px = ( hasval( cursorOptions ) && hasval( cursorOptions.boxSize_px ) ? cursorOptions.boxSize_px : 8 );
- var crosshairThickness_px = ( hasval( cursorOptions ) && hasval( cursorOptions.crosshairThickness_px ) ? cursorOptions.boxSize_px : 2 );
- var boxThickness_px = ( hasval( cursorOptions ) && hasval( cursorOptions.boxThickness_px ) ? cursorOptions.boxSize_px : 2 );
-
- var program = new Program( xyFrac_VERTSHADER, solid_FRAGSHADER );
- var u_Color = new UniformColor( program, 'u_Color' );
- var a_Position = new Attribute( program, 'a_XyFrac' );
-
- var xys = new Float32Array( 0 );
- xys = ensureCapacityFloat32( xys, 4 );
- var xysBuffer = newDynamicBuffer( );
-
- var textTextures = newTextTextureCache2( font );
- var textureRenderer = new TextureRenderer( );
-
- // Painter
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
-
- // only draw a cursor if we are the current hovered row
- var hoveredRow : TimelineRowModel = ui.selection.hoveredRow.value;
- if ( !hasval( hoveredRow ) || hoveredRow.rowGuid !== rowModel.rowGuid ) return;
-
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
-
- var indexXys = 0;
- textTextures.resetTouches( );
-
- var time = ui.selection.hoveredTime_PMILLIS.value;
- var y = ui.selection.hoveredY.value;
-
- if ( !hasval( time ) || !hasval( y ) ) return;
-
- var wLine = crosshairThickness_px / viewport.w;
- var hLine = crosshairThickness_px / viewport.h;
-
- var wBoxLine = boxThickness_px / viewport.w;
- var hBoxLine = boxThickness_px / viewport.h;
-
- var wBox = boxSize_px / viewport.w;
- var hBox = boxSize_px / viewport.h;
-
- if ( hasval( time ) ) {
-
- var cursorModel = model.cursor( rowModel.cursorGuid );
-
- if ( hasval( cursorModel) )
- {
- if ( hasval( cursorModel.lineColor ) )
- {
- lineColor = cursorModel.lineColor;
- }
+import { Color, white } from '../color';
+import { TimelineTimeseriesPainterFactory, TimelineTimeseriesPainterOptions } from './timeline_timeseries_row';
+import { Drawable, Painter } from '../core';
+import { TimeAxis1D } from './time_axis';
+import { Axis1D } from '../plot/axis';
+import { TimelineModel, TimelineRowModel, TimelineTimeseriesFragmentModel } from './timeline_model';
+import { TimelineUi } from './timeline_ui';
+import { Program, UniformColor, Attribute } from '../shader';
+import { xyFrac_VERTSHADER, solid_FRAGSHADER, putQuadXys } from '../misc';
+import { ensureCapacityFloat32, GL, hasval } from '../util/util';
+import { newDynamicBuffer } from '../buffer';
+import { newTextTextureCache2 } from '../text';
+import { TextureRenderer } from '../texture';
+import { BoundsUnmodifiable } from '../bounds';
+import { indexAtOrBefore, indexAtOrAfter } from '../util/sorted_arrays';
+
+export interface TimeseriesCursorPainterOptions {
+ font?: string;
+ textColor?: Color;
+ // number of pixels between box and label text
+ buffer_px?: number;
+ // number of significant digits in text labels
+ textDecimals?: number;
+
+ // color of selection box and crosshair lines
+ lineColor?: Color;
+
+ // thickness of cursor crosshair lines in pixels
+ crosshairThickness_px?: number;
+
+ // width/height of selection box (shown at intersection beween crosshairs and timeseries) in pixels
+ boxSize_px?: number;
+ // thickness of selection box lines in pixels
+ boxThickness_px?: number;
+}
+
+export function newTimeseriesCursorPainterFactory(cursorOptions?: TimeseriesCursorPainterOptions): TimelineTimeseriesPainterFactory {
+
+ // Painter Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, dataAxis: Axis1D, model: TimelineModel, rowModel: TimelineRowModel, ui: TimelineUi, options: TimelineTimeseriesPainterOptions): Painter {
+
+ let textColor = (hasval(cursorOptions) && hasval(cursorOptions.textColor) ? cursorOptions.textColor : white);
+ let lineColor = (hasval(cursorOptions) && hasval(cursorOptions.lineColor) ? cursorOptions.lineColor : white);
+ const font = (hasval(cursorOptions) && hasval(cursorOptions.font) ? cursorOptions.font : options.timelineFont);
+ const buffer_px = (hasval(cursorOptions) && hasval(cursorOptions.buffer_px) ? cursorOptions.buffer_px : 4);
+ const textDecimals = (hasval(cursorOptions) && hasval(cursorOptions.textDecimals) ? cursorOptions.textDecimals : 2);
+ const boxSize_px = (hasval(cursorOptions) && hasval(cursorOptions.boxSize_px) ? cursorOptions.boxSize_px : 8);
+ const crosshairThickness_px = (hasval(cursorOptions) && hasval(cursorOptions.crosshairThickness_px) ? cursorOptions.boxSize_px : 2);
+ const boxThickness_px = (hasval(cursorOptions) && hasval(cursorOptions.boxThickness_px) ? cursorOptions.boxSize_px : 2);
+
+ const program = new Program(xyFrac_VERTSHADER, solid_FRAGSHADER);
+ const u_Color = new UniformColor(program, 'u_Color');
+ const a_Position = new Attribute(program, 'a_XyFrac');
+
+ let xys = new Float32Array(0);
+ xys = ensureCapacityFloat32(xys, 4);
+ const xysBuffer = newDynamicBuffer();
+
+ const textTextures = newTextTextureCache2(font);
+ const textureRenderer = new TextureRenderer();
+
+ // Painter
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+
+ // only draw a cursor if we are the current hovered row
+ const hoveredRow: TimelineRowModel = ui.selection.hoveredRow.value;
+ if (!hasval(hoveredRow) || hoveredRow.rowGuid !== rowModel.rowGuid) {
+ return;
+ }
+
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ let indexXys = 0;
+ textTextures.resetTouches();
+
+ const time = ui.selection.hoveredTime_PMILLIS.value;
+ const y = ui.selection.hoveredY.value;
+
+ if (!hasval(time) || !hasval(y)) {
+ return;
+ }
+
+ const wLine = crosshairThickness_px / viewport.w;
+ const hLine = crosshairThickness_px / viewport.h;
+
+ const wBoxLine = boxThickness_px / viewport.w;
+ const hBoxLine = boxThickness_px / viewport.h;
+
+ const wBox = boxSize_px / viewport.w;
+ const hBox = boxSize_px / viewport.h;
+
+ if (hasval(time)) {
- if ( hasval( cursorModel.textColor ) )
- {
- textColor = cursorModel.textColor;
+ const cursorModel = model.cursor(rowModel.cursorGuid);
+
+ if (hasval(cursorModel)) {
+ if (hasval(cursorModel.lineColor)) {
+ lineColor = cursorModel.lineColor;
+ }
+
+ if (hasval(cursorModel.textColor)) {
+ textColor = cursorModel.textColor;
+ }
+
+ textureRenderer.begin(gl, viewport);
+
+ const timeseriesCount = cursorModel.labeledTimeseriesGuids.length;
+
+ // 36 vertices for crosshairs, 48 vertices per timeseries intersection marker
+ xys = ensureCapacityFloat32(xys, 2 * (36 + timeseriesCount * 48));
+
+ for (let i = 0; i < cursorModel.labeledTimeseriesGuids.length; i++) {
+
+ const timeseriesGuid = cursorModel.labeledTimeseriesGuids.valueAt(i);
+ const timeseries = model.timeseries(timeseriesGuid);
+
+ // if the row doesn't contain the timeseries, don't show cursor intersections
+ if (!rowModel.timeseriesGuids.hasValue(timeseriesGuid)) {
+ continue;
}
- textureRenderer.begin( gl, viewport );
-
- var timeseriesCount = cursorModel.labeledTimeseriesGuids.length;
-
- // 36 vertices for crosshairs, 48 vertices per timeseries intersection marker
- xys = ensureCapacityFloat32( xys, 2 * ( 36 + timeseriesCount * 48 ) );
-
- for ( var i = 0 ; i < cursorModel.labeledTimeseriesGuids.length ; i++ ) {
-
- var timeseriesGuid = cursorModel.labeledTimeseriesGuids.valueAt( i );
- var timeseries = model.timeseries( timeseriesGuid );
-
- // if the row doesn't contain the timeseries, don't show cursor intersections
- if ( !rowModel.timeseriesGuids.hasValue( timeseriesGuid ) ) continue;
-
- for ( var j = 0 ; j < timeseries.fragmentGuids.length ; j++ ) {
- var fragmentGuid : string = timeseries.fragmentGuids.valueAt( j );
- var fragment : TimelineTimeseriesFragmentModel = model.timeseriesFragment( fragmentGuid );
-
- // fragments should not overlap
- if ( fragment.start_PMILLIS < time && fragment.end_PMILLIS > time ) {
-
- var value : number;
-
- // bars are drawn starting at the point and continuing to the next point, so we don't interpolate them
- if ( timeseries.uiHint == 'bars' ) {
- var index : number = indexAtOrBefore( fragment.times_PMILLIS, time );
- value = fragment.data[index];
- }
- else {
- var index0 : number = indexAtOrBefore( fragment.times_PMILLIS, time );
- var index1 : number = indexAtOrAfter( fragment.times_PMILLIS, time );
-
- var value0 = fragment.data[index0];
- var time0 = fragment.times_PMILLIS[index0];
-
- var value1 = fragment.data[index1];
- var time1 = fragment.times_PMILLIS[index1];
-
- var diff = time1 - time0;
- var diff0 = ( time - time0 ) / diff;
- var diff1 = 1 - diff0;
-
- value = value0 * diff1 + value1 * diff0;
- }
-
- var textTexture = textTextures.value( textColor.rgbaString, value.toFixed( textDecimals ) );
-
- var valueFracY = dataAxis.vFrac( value );
- var valueFracX = timeAxis.tFrac( time );
-
- var boxLeft = valueFracX - wBox/2;
- var boxRight = valueFracX + wBox/2;
- var boxTop = valueFracY + hBox/2;
- var boxBottom = valueFracY - hBox/2;
-
- // draw box at value location
-
- // left edge
- indexXys = putQuadXys( xys, indexXys, boxLeft-wBoxLine/2, boxLeft+wBoxLine/2, boxTop+hBoxLine/2, boxBottom-hBoxLine/2 );
- // right edge
- indexXys = putQuadXys( xys, indexXys, boxRight-wBoxLine/2, boxRight+wBoxLine/2, boxTop+hBoxLine/2, boxBottom-hBoxLine/2 );
- // top edge
- indexXys = putQuadXys( xys, indexXys, boxLeft+wBoxLine/2, boxRight-wBoxLine/2, boxTop-hBoxLine/2, boxTop+hBoxLine/2 );
- // bottom edge
- indexXys = putQuadXys( xys, indexXys, boxLeft+wBoxLine/2, boxRight-wBoxLine/2, boxBottom-hBoxLine/2, boxBottom+hBoxLine/2 );
-
- // draw text
- //XXX 0.6 looks more centered to the eye than 0.5 for numeric text
- textureRenderer.draw( gl, textTexture, boxRight+wBoxLine/2+buffer_px/viewport.w, valueFracY, { xAnchor: 0, yAnchor: .6 } );
+ for (let j = 0; j < timeseries.fragmentGuids.length; j++) {
+ const fragmentGuid: string = timeseries.fragmentGuids.valueAt(j);
+ const fragment: TimelineTimeseriesFragmentModel = model.timeseriesFragment(fragmentGuid);
+
+ // fragments should not overlap
+ if (fragment.start_PMILLIS < time && fragment.end_PMILLIS > time) {
+ let value: number;
+
+ // bars are drawn starting at the point and continuing to the next point, so we don't interpolate them
+ if (timeseries.uiHint === 'bars') {
+ const index: number = indexAtOrBefore(fragment.times_PMILLIS, time);
+ value = fragment.data[index];
}
- }
- }
+ else {
+ const index0: number = indexAtOrBefore(fragment.times_PMILLIS, time);
+ const index1: number = indexAtOrAfter(fragment.times_PMILLIS, time);
- if ( hasval( cursorModel.showCursorText ) ? cursorModel.showCursorText : true ) {
- var textTexture = textTextures.value( textColor.rgbaString, y.toFixed( textDecimals ) );
- textureRenderer.draw( gl, textTexture, 1, dataAxis.vFrac( y ) + buffer_px/viewport.h, { xAnchor: 1, yAnchor: 0 } );
- }
+ const value0 = fragment.data[index0];
+ const time0 = fragment.times_PMILLIS[index0];
- textureRenderer.end( gl );
- textTextures.retainTouched( );
+ const value1 = fragment.data[index1];
+ const time1 = fragment.times_PMILLIS[index1];
- var xLeft = 0;
- var xRight = 1;
- var yMid = dataAxis.vFrac( y );
- var xMid = timeAxis.tFrac( time );
+ const diff = time1 - time0;
+ const diff0 = (time - time0) / diff;
+ const diff1 = 1 - diff0;
- // draw horizontal line
- if ( hasval( cursorModel.showHorizontalLine ) ? cursorModel.showHorizontalLine : true ) {
- indexXys = putQuadXys( xys, indexXys, xLeft, xRight, yMid-hLine/2, yMid+hLine/2 );
- }
+ value = value0 * diff1 + value1 * diff0;
+ }
+
+ const textTexture = textTextures.value(textColor.rgbaString, value.toFixed(textDecimals));
+
+ const valueFracY = dataAxis.vFrac(value);
+ const valueFracX = timeAxis.tFrac(time);
+
+ const boxLeft = valueFracX - wBox / 2;
+ const boxRight = valueFracX + wBox / 2;
+ const boxTop = valueFracY + hBox / 2;
+ const boxBottom = valueFracY - hBox / 2;
+
+ // draw box at value location
+
+ // left edge
+ indexXys = putQuadXys(xys, indexXys, boxLeft - wBoxLine / 2, boxLeft + wBoxLine / 2, boxTop + hBoxLine / 2, boxBottom - hBoxLine / 2);
+ // right edge
+ indexXys = putQuadXys(xys, indexXys, boxRight - wBoxLine / 2, boxRight + wBoxLine / 2, boxTop + hBoxLine / 2, boxBottom - hBoxLine / 2);
+ // top edge
+ indexXys = putQuadXys(xys, indexXys, boxLeft + wBoxLine / 2, boxRight - wBoxLine / 2, boxTop - hBoxLine / 2, boxTop + hBoxLine / 2);
+ // bottom edge
+ indexXys = putQuadXys(xys, indexXys, boxLeft + wBoxLine / 2, boxRight - wBoxLine / 2, boxBottom - hBoxLine / 2, boxBottom + hBoxLine / 2);
- // draw vertical lines (split in two to avoid overlap with horizontal)
- if ( hasval( cursorModel.showVerticalLine ) ? cursorModel.showVerticalLine : true ) {
- indexXys = putQuadXys( xys, indexXys, xMid-wLine/2, xMid+wLine/2, 0, yMid-hLine/2 );
- indexXys = putQuadXys( xys, indexXys, xMid-wLine/2, xMid+wLine/2, yMid+hLine/2, 1 );
+ // draw text
+ // XXX 0.6 looks more centered to the eye than 0.5 for numeric text
+ textureRenderer.draw(gl, textTexture, boxRight + wBoxLine / 2 + buffer_px / viewport.w, valueFracY, { xAnchor: 0, yAnchor: .6 });
+
+ }
}
+ }
+
+ if (hasval(cursorModel.showCursorText) ? cursorModel.showCursorText : true) {
+ const textTexture = textTextures.value(textColor.rgbaString, y.toFixed(textDecimals));
+ textureRenderer.draw(gl, textTexture, 1, dataAxis.vFrac(y) + buffer_px / viewport.h, { xAnchor: 1, yAnchor: 0 });
+ }
+
+ textureRenderer.end(gl);
+ textTextures.retainTouched();
- // draw lines
- program.use( gl );
+ const xLeft = 0;
+ const xRight = 1;
+ const yMid = dataAxis.vFrac(y);
+ const xMid = timeAxis.tFrac(time);
- xysBuffer.setData( xys.subarray( 0, indexXys ) );
- a_Position.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
- u_Color.setData( gl, lineColor );
- gl.drawArrays( GL.TRIANGLES, 0, Math.floor( indexXys / 2 ) );
-
- a_Position.disable( gl );
- program.endUse( gl );
+ // draw horizontal line
+ if (hasval(cursorModel.showHorizontalLine) ? cursorModel.showHorizontalLine : true) {
+ indexXys = putQuadXys(xys, indexXys, xLeft, xRight, yMid - hLine / 2, yMid + hLine / 2);
}
+
+ // draw vertical lines (split in two to avoid overlap with horizontal)
+ if (hasval(cursorModel.showVerticalLine) ? cursorModel.showVerticalLine : true) {
+ indexXys = putQuadXys(xys, indexXys, xMid - wLine / 2, xMid + wLine / 2, 0, yMid - hLine / 2);
+ indexXys = putQuadXys(xys, indexXys, xMid - wLine / 2, xMid + wLine / 2, yMid + hLine / 2, 1);
+ }
+
+ // draw lines
+ program.use(gl);
+
+ xysBuffer.setData(xys.subarray(0, indexXys));
+ a_Position.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+ u_Color.setData(gl, lineColor);
+ gl.drawArrays(GL.TRIANGLES, 0, Math.floor(indexXys / 2));
+
+ a_Position.disable(gl);
+ program.endUse(gl);
}
}
- }
- }
-}
\ No newline at end of file
+ };
+ };
+}
diff --git a/src/webglimpse/timeline/timeline_event_style.ts b/src/webglimpse/timeline/timeline_event_style.ts
index 95a452d..e57bc45 100644
--- a/src/webglimpse/timeline/timeline_event_style.ts
+++ b/src/webglimpse/timeline/timeline_event_style.ts
@@ -27,97 +27,94 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+export interface TimelineEventIcon {
+ url: string;
+ displayWidth: number; // horizontal size of icon in pixels
+ displayHeight: number; // vertical size of icon in pixels
+ hAlign: number; // relative location of center pixel of icon (0=left side, 1=right side)
+ hPos: number; // relative icon position along event bar (0=left side, 1=right side)
+}
- export interface TimelineEventIcon {
- url : string;
- displayWidth : number; // horizontal size of icon in pixels
- displayHeight : number; // vertical size of icon in pixels
- hAlign : number; // relative location of center pixel of icon (0=left side, 1=right side)
- hPos : number; // relative icon position along event bar (0=left side, 1=right side)
- }
+export interface TimelineEventStyle {
+ styleGuid: string;
+ icons: TimelineEventIcon[];
+}
- export interface TimelineEventStyle {
- styleGuid : string;
- icons : TimelineEventIcon[];
- }
+export class TimelineEventIconUi {
+ private _url: string;
+ private _displayWidth: number;
+ private _displayHeight: number;
+ private _hAlign: number;
+ private _hPos: number;
- export class TimelineEventIconUi {
- private _url : string;
- private _displayWidth : number;
- private _displayHeight : number;
- private _hAlign : number;
- private _hPos : number;
-
- constructor( icon : TimelineEventIcon ) {
- this._setAttrs( icon );
- }
-
- private _setAttrs( icon : TimelineEventIcon ) {
- this._url = icon.url;
- this._displayWidth = icon.displayWidth;
- this._displayHeight = icon.displayHeight;
- this._hAlign = icon.hAlign;
- this._hPos = icon.hPos;
- }
-
- get url( ) : string { return this._url; }
- get displayWidth( ) : number { return this._displayWidth; }
- get displayHeight( ) : number { return this._displayHeight; }
- get hAlign( ) : number { return this._hAlign; }
- get hPos( ) : number { return this._hPos; }
-
- snapshot( ) : TimelineEventIcon {
- return {
- url: this._url,
- displayWidth: this._displayWidth,
- displayHeight: this._displayHeight,
- hAlign: this._hAlign,
- hPos: this._hPos
- };
- }
+ constructor(icon: TimelineEventIcon) {
+ this._setAttrs(icon);
}
+ private _setAttrs(icon: TimelineEventIcon) {
+ this._url = icon.url;
+ this._displayWidth = icon.displayWidth;
+ this._displayHeight = icon.displayHeight;
+ this._hAlign = icon.hAlign;
+ this._hPos = icon.hPos;
+ }
+ get url(): string { return this._url; }
+ get displayWidth(): number { return this._displayWidth; }
+ get displayHeight(): number { return this._displayHeight; }
+ get hAlign(): number { return this._hAlign; }
+ get hPos(): number { return this._hPos; }
+
+ snapshot(): TimelineEventIcon {
+ return {
+ url: this._url,
+ displayWidth: this._displayWidth,
+ displayHeight: this._displayHeight,
+ hAlign: this._hAlign,
+ hPos: this._hPos
+ };
+ }
+}
- export class TimelineEventStyleUi {
- private _styleGuid : string;
- private _icons : TimelineEventIconUi[];
- constructor( style : TimelineEventStyle ) {
- this._styleGuid = style.styleGuid;
- this._setAttrs( style );
- }
- get styleGuid( ) : string {
- return this._styleGuid;
- }
+export class TimelineEventStyleUi {
+ private _styleGuid: string;
+ private _icons: TimelineEventIconUi[];
- private _setAttrs( style : TimelineEventStyle ) {
- this._icons = style.icons.map( ( icon )=>{ return new TimelineEventIconUi( icon ); } );
- }
+ constructor(style: TimelineEventStyle) {
+ this._styleGuid = style.styleGuid;
+ this._setAttrs(style);
+ }
- get numIcons( ) : number {
- return this._icons.length;
- }
+ get styleGuid(): string {
+ return this._styleGuid;
+ }
- icon( index : number ) : TimelineEventIconUi {
- return this._icons[ index ];
- }
+ private _setAttrs(style: TimelineEventStyle) {
+ this._icons = style.icons.map((icon) => new TimelineEventIconUi(icon));
+ }
+
+ get numIcons(): number {
+ return this._icons.length;
+ }
+
+ icon(index: number): TimelineEventIconUi {
+ return this._icons[index];
+ }
- snapshot( ) : TimelineEventStyle {
- return {
- styleGuid: this._styleGuid,
- icons: this._icons.map( (ui)=>ui.snapshot() )
- };
- }
+ snapshot(): TimelineEventStyle {
+ return {
+ styleGuid: this._styleGuid,
+ icons: this._icons.map((ui) => ui.snapshot())
+ };
}
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/timeline/timeline_events_row.ts b/src/webglimpse/timeline/timeline_events_row.ts
index d5e71fe..22eda56 100644
--- a/src/webglimpse/timeline/timeline_events_row.ts
+++ b/src/webglimpse/timeline/timeline_events_row.ts
@@ -27,1921 +27,1942 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
+import { Color, darker } from '../color';
+import { Drawable, Painter, Pane, PointerEvent, Mask2D, isLeftMouseDown, Layout } from '../core';
+import { TimeAxis1D } from './time_axis';
+import { TimelineLaneArray, TimelineLane, effectiveEdges_PMILLIS } from './timeline_lanes';
+import { TimelineUi } from './timeline_ui';
+import { TimelineRowPaneFactory, TimelineRowPaneOptions } from './timeline_row';
+import { Axis1D } from '../plot/axis';
+import { TimelineModel, TimelineRowModel, TimelineEventModel } from './timeline_model';
+import { BoundsUnmodifiable, Size } from '../bounds';
+import { StringMap, concatLines, GL, ensureCapacityFloat32, hasval, isNumber } from '../util/util';
+import { indexNearest } from '../util/sorted_arrays';
+import { Program, Attribute, Uniform1f } from '../shader';
+import { newDynamicBuffer } from '../buffer';
+import { putQuadXys, putRgbas, putQuadRgbas, putUpperRightTriangleXys, putLowerRightTriangleXys, putLowerLeftTriangleXys, putUpperLeftTriangleXys, varyingColor_FRAGSHADER } from '../misc';
+import { OrderedSet } from '../util/ordered_set';
+import { TextureRenderer, Texture2D } from '../texture';
+import { TwoKeyCache } from '../util/multikey_cache';
+import { TextTexture2D, newTextTextureCache2 } from '../text';
+
+
+export interface TimelineEventsPainterOptions {
+ timelineFont: string;
+ timelineFgColor: Color;
+ rowTopPadding: number;
+ rowBottomPadding: number;
+ laneHeight: number;
+}
- export interface TimelineEventsPainterOptions {
- timelineFont : string;
- timelineFgColor : Color;
- rowTopPadding : number;
- rowBottomPadding : number;
- laneHeight : number;
- }
+export type TimelineEventsPainterFactory = (drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions) => Painter;
- export interface TimelineEventsPainterFactory {
- ( drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) : Painter;
- }
+export interface TimelineEventsRowPaneOptions {
+ rowTopPadding?: number;
+ rowBottomPadding?: number;
+ laneHeight?: number;
+ allowMultipleLanes?: boolean;
+ painterFactories?: TimelineEventsPainterFactory[];
+}
- export interface TimelineEventsRowPaneOptions {
- rowTopPadding? : number;
- rowBottomPadding? : number;
- laneHeight? : number;
- allowMultipleLanes? : boolean;
- painterFactories? : TimelineEventsPainterFactory[];
- }
+export function newEventsRowPaneFactory(eventsRowOpts?: TimelineEventsRowPaneOptions): TimelineRowPaneFactory {
- export function newEventsRowPaneFactory( eventsRowOpts? : TimelineEventsRowPaneOptions ) : TimelineRowPaneFactory {
+ // Pane Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, dataAxis: Axis1D, model: TimelineModel, row: TimelineRowModel, ui: TimelineUi, options: TimelineRowPaneOptions): Pane {
+ const rowTopPadding = (hasval(eventsRowOpts) && hasval(eventsRowOpts.rowTopPadding) ? eventsRowOpts.rowTopPadding : 6);
+ const rowBottomPadding = (hasval(eventsRowOpts) && hasval(eventsRowOpts.rowBottomPadding) ? eventsRowOpts.rowBottomPadding : 6);
+ const laneHeight = (hasval(eventsRowOpts) && hasval(eventsRowOpts.laneHeight) ? eventsRowOpts.laneHeight : 33);
+ const painterFactories = (hasval(eventsRowOpts) && hasval(eventsRowOpts.painterFactories) ? eventsRowOpts.painterFactories : []);
+ const allowMultipleLanes = (hasval(eventsRowOpts) && hasval(eventsRowOpts.allowMultipleLanes) ? eventsRowOpts.allowMultipleLanes : true);
- // Pane Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, dataAxis : Axis1D, model : TimelineModel, row : TimelineRowModel, ui : TimelineUi, options : TimelineRowPaneOptions ) : Pane {
- var rowTopPadding = ( hasval( eventsRowOpts ) && hasval( eventsRowOpts.rowTopPadding ) ? eventsRowOpts.rowTopPadding : 6 );
- var rowBottomPadding = ( hasval( eventsRowOpts ) && hasval( eventsRowOpts.rowBottomPadding ) ? eventsRowOpts.rowBottomPadding : 6 );
- var laneHeight = ( hasval( eventsRowOpts ) && hasval( eventsRowOpts.laneHeight ) ? eventsRowOpts.laneHeight : 33 );
- var painterFactories = ( hasval( eventsRowOpts ) && hasval( eventsRowOpts.painterFactories ) ? eventsRowOpts.painterFactories : [] );
- var allowMultipleLanes = ( hasval( eventsRowOpts ) && hasval( eventsRowOpts.allowMultipleLanes ) ? eventsRowOpts.allowMultipleLanes : true );
+ const timelineFont = options.timelineFont;
+ const timelineFgColor = options.timelineFgColor;
+ const draggableEdgeWidth = options.draggableEdgeWidth;
+ const snapToDistance = options.snapToDistance;
- var timelineFont = options.timelineFont;
- var timelineFgColor = options.timelineFgColor;
- var draggableEdgeWidth = options.draggableEdgeWidth;
- var snapToDistance = options.snapToDistance;
+ const rowUi = ui.rowUi(row.rowGuid);
+ const input = ui.input;
+ const selection = ui.selection;
- var rowUi = ui.rowUi( row.rowGuid );
- var input = ui.input;
- var selection = ui.selection;
-
- var lanes = new TimelineLaneArray( model, row, ui, allowMultipleLanes );
+ const lanes = new TimelineLaneArray(model, row, ui, allowMultipleLanes);
- var timeAtCoords_PMILLIS = function( viewport : BoundsUnmodifiable, i : number ) : number {
- return timeAxis.tAtFrac_PMILLIS( viewport.xFrac( i ) );
- };
+ const timeAtCoords_PMILLIS = function (viewport: BoundsUnmodifiable, i: number): number {
+ return timeAxis.tAtFrac_PMILLIS(viewport.xFrac(i));
+ };
- var timeAtPointer_PMILLIS = function( ev : PointerEvent ) : number {
- return timeAtCoords_PMILLIS( ev.paneViewport, ev.i );
- };
+ const timeAtPointer_PMILLIS = function (ev: PointerEvent): number {
+ return timeAtCoords_PMILLIS(ev.paneViewport, ev.i);
+ };
- var eventAtCoords = function( viewport : BoundsUnmodifiable, i : number, j : number ) : TimelineEventModel {
- var laneNum = Math.floor( ( viewport.jEnd - j - rowTopPadding ) / laneHeight );
- var time_PMILLIS = timeAtCoords_PMILLIS( viewport, i );
- return lanes.eventAt( laneNum, time_PMILLIS );
- };
+ const eventAtCoords = function (viewport: BoundsUnmodifiable, i: number, j: number): TimelineEventModel {
+ const laneNum = Math.floor((viewport.jEnd - j - rowTopPadding) / laneHeight);
+ const time_PMILLIS = timeAtCoords_PMILLIS(viewport, i);
+ return lanes.eventAt(laneNum, time_PMILLIS);
+ };
- var eventAtPointer = function( ev : PointerEvent ) : TimelineEventModel {
- return eventAtCoords( ev.paneViewport, ev.i, ev.j );
- };
+ const eventAtPointer = function (ev: PointerEvent): TimelineEventModel {
+ return eventAtCoords(ev.paneViewport, ev.i, ev.j);
+ };
- var isInsideAnEvent : Mask2D = function( viewport : BoundsUnmodifiable, i : number, j : number ) : boolean {
- return hasval( eventAtCoords( viewport, i, j ) );
- };
+ const isInsideAnEvent: Mask2D = function (viewport: BoundsUnmodifiable, i: number, j: number): boolean {
+ return hasval(eventAtCoords(viewport, i, j));
+ };
- // Create pane
- //
+ // Create pane
+ //
- var layout = {
- updatePrefSize: function( parentPrefSize : Size ) {
- parentPrefSize.h = rowTopPadding + rowBottomPadding + Math.max( 1, lanes.length )*laneHeight;
- parentPrefSize.w = null;
- }
- };
- var rowContentPane = new Pane( layout, true, isInsideAnEvent );
-
- rowUi.addPane( 'content', rowContentPane );
-
- var painterOptions = { timelineFont: timelineFont, timelineFgColor: timelineFgColor, rowTopPadding: rowTopPadding, rowBottomPadding: rowBottomPadding, laneHeight: laneHeight };
- for ( var n = 0; n < painterFactories.length; n++ ) {
- var createPainter = painterFactories[ n ];
- rowContentPane.addPainter( createPainter( drawable, timeAxis, lanes, ui, painterOptions ) );
+ const layout = {
+ updatePrefSize: function (parentPrefSize: Size): void {
+ parentPrefSize.h = rowTopPadding + rowBottomPadding + Math.max(1, lanes.length) * laneHeight;
+ parentPrefSize.w = null;
}
+ };
+ const rowContentPane = new Pane(layout, true, isInsideAnEvent);
+ rowUi.addPane('content', rowContentPane);
- var redraw = function( ) {
- drawable.redraw( );
- };
+ const painterOptions = { timelineFont: timelineFont, timelineFgColor: timelineFgColor, rowTopPadding: rowTopPadding, rowBottomPadding: rowBottomPadding, laneHeight: laneHeight };
+ for (let n = 0; n < painterFactories.length; n++) {
+ const createPainter = painterFactories[n];
+ rowContentPane.addPainter(createPainter(drawable, timeAxis, lanes, ui, painterOptions));
+ }
- row.eventGuids.valueAdded.on( redraw );
- row.eventGuids.valueMoved.on( redraw );
- row.eventGuids.valueRemoved.on( redraw );
- var watchEventAttrs = function( eventGuid : string ) {
- model.event( eventGuid ).attrsChanged.on( redraw );
- };
- row.eventGuids.forEach( watchEventAttrs );
- row.eventGuids.valueAdded.on( watchEventAttrs );
+ const redraw = function () {
+ drawable.redraw();
+ };
- var removeRedraw = function( eventGuid : string ) {
- model.event( eventGuid ).attrsChanged.off( redraw );
- }
- row.eventGuids.valueRemoved.on( removeRedraw );
+ row.eventGuids.valueAdded.on(redraw);
+ row.eventGuids.valueMoved.on(redraw);
+ row.eventGuids.valueRemoved.on(redraw);
+ const watchEventAttrs = function (eventGuid: string) {
+ model.event(eventGuid).attrsChanged.on(redraw);
+ };
+ row.eventGuids.forEach(watchEventAttrs);
+ row.eventGuids.valueAdded.on(watchEventAttrs);
+ const removeRedraw = function (eventGuid: string) {
+ model.event(eventGuid).attrsChanged.off(redraw);
+ };
+ row.eventGuids.valueRemoved.on(removeRedraw);
- // Used by both sets of listeners to know whether an event-drag is in progress
- var eventDragMode : string = null;
+ // Used by both sets of listeners to know whether an event-drag is in progress
+ let eventDragMode: string = null;
- // Hook up input notifications
- //
- var recentMouseMove : PointerEvent = null;
- rowContentPane.mouseMove.on( function( ev : PointerEvent ) {
- input.mouseMove.fire( ev );
- if ( !eventDragMode ) {
- input.timeHover.fire( timeAtPointer_PMILLIS( ev ), ev );
- input.rowHover.fire( row, ev );
- input.eventHover.fire( eventAtPointer( ev ), ev );
- }
- recentMouseMove = ev;
- } );
-
- rowContentPane.mouseExit.on( function( ev : PointerEvent ) {
- input.mouseExit.fire( ev );
- if ( !eventDragMode ) {
- input.timeHover.fire( null, ev );
- input.rowHover.fire( null, ev );
- input.eventHover.fire( null, ev );
- }
- recentMouseMove = null;
- } );
-
- var uiMillisPerPxChanged = function( ) {
- if ( !eventDragMode && recentMouseMove != null ) {
- var ev = recentMouseMove;
- input.timeHover.fire( timeAtPointer_PMILLIS( ev ), ev );
- input.eventHover.fire( eventAtPointer( ev ), ev );
- }
- };
- ui.millisPerPx.changed.on( uiMillisPerPxChanged );
-
- rowContentPane.mouseUp.on( function( ev : PointerEvent ) {
- input.mouseUp.fire( ev );
- } );
+ // Hook up input notifications
+ //
+
+ let recentMouseMove: PointerEvent = null;
- rowContentPane.mouseDown.on( function( ev : PointerEvent ) {
- input.mouseDown.fire( ev );
- } );
+ rowContentPane.mouseMove.on(function (ev: PointerEvent) {
+ input.mouseMove.fire(ev);
+ if (!eventDragMode) {
+ input.timeHover.fire(timeAtPointer_PMILLIS(ev), ev);
+ input.rowHover.fire(row, ev);
+ input.eventHover.fire(eventAtPointer(ev), ev);
+ }
+ recentMouseMove = ev;
+ });
+
+ rowContentPane.mouseExit.on(function (ev: PointerEvent) {
+ input.mouseExit.fire(ev);
+ if (!eventDragMode) {
+ input.timeHover.fire(null, ev);
+ input.rowHover.fire(null, ev);
+ input.eventHover.fire(null, ev);
+ }
+ recentMouseMove = null;
+ });
+
+ const uiMillisPerPxChanged = function () {
+ if (!eventDragMode && recentMouseMove != null) {
+ const ev = recentMouseMove;
+ input.timeHover.fire(timeAtPointer_PMILLIS(ev), ev);
+ input.eventHover.fire(eventAtPointer(ev), ev);
+ }
+ };
+ ui.millisPerPx.changed.on(uiMillisPerPxChanged);
- rowContentPane.mouseWheel.on( options.mouseWheelListener );
+ rowContentPane.mouseUp.on(function (ev: PointerEvent) {
+ input.mouseUp.fire(ev);
+ });
- rowContentPane.contextMenu.on( function( ev : PointerEvent ) {
- input.contextMenu.fire( ev );
- } );
+ rowContentPane.mouseDown.on(function (ev: PointerEvent) {
+ input.mouseDown.fire(ev);
+ });
+ rowContentPane.mouseWheel.on(options.mouseWheelListener);
+ rowContentPane.contextMenu.on(function (ev: PointerEvent) {
+ input.contextMenu.fire(ev);
+ });
- // Begin event-drag
- //
- var eventDragEvents : TimelineEventModel[] = [];
- var eventDragOffsets_MILLIS : StringMap = {};
- var eventDragSnapTimes_PMILLIS : number[] = [];
+ // Begin event-drag
+ //
- // Event-edges are draggable for events at least this wide
- var minEventWidthForEdgeDraggability = 3 * draggableEdgeWidth;
+ let eventDragEvents: TimelineEventModel[] = [];
+ let eventDragOffsets_MILLIS: StringMap = {};
+ let eventDragSnapTimes_PMILLIS: number[] = [];
- // When dragging an event-edge, the event cannot be made narrower than this
- //
- // Needs to be greater than minEventWidthForEdgeDraggability -- by enough to
- // cover floating-point precision loss -- so a user can't accidentally make
- // an event so narrow that it can't easily be widened again.
- //
- var minEventWidthWhenDraggingEdge = minEventWidthForEdgeDraggability + 1;
+ // Event-edges are draggable for events at least this wide
+ const minEventWidthForEdgeDraggability = 3 * draggableEdgeWidth;
- function allUserEditable( events : TimelineEventModel[] ) : boolean {
- for ( var n = 0; n < events.length; n++ ) {
- if ( !events[ n ].userEditable ) {
- return false;
- }
+ // When dragging an event-edge, the event cannot be made narrower than this
+ //
+ // Needs to be greater than minEventWidthForEdgeDraggability -- by enough to
+ // cover floating-point precision loss -- so a user can't accidentally make
+ // an event so narrow that it can't easily be widened again.
+ //
+ const minEventWidthWhenDraggingEdge = minEventWidthForEdgeDraggability + 1;
+
+
+ function allUserEditable(events: TimelineEventModel[]): boolean {
+ for (let n = 0; n < events.length; n++) {
+ if (!events[n].userEditable) {
+ return false;
}
- return true;
}
+ return true;
+ }
- function chooseEventDragMode( ui : TimelineUi, mouseTime_PMILLIS : number, eventDragEvents : TimelineEventModel[] ) : string {
- if ( eventDragEvents.length === 0 ) {
- // If no events are selected, then we don't have any to drag
- return null;
- }
- else if ( !allUserEditable( eventDragEvents ) ) {
- // If any selected event is not user-editable, don't allow editing
- return 'undraggable';
- }
- else if ( eventDragEvents.length > 1 ) {
- // If more than one event is selected, don't allow edge dragging
+ function chooseEventDragMode(timelineUi: TimelineUi, mouseTime_PMILLIS: number, timelineEventDragEvents: TimelineEventModel[]): string {
+ if (timelineEventDragEvents.length === 0) {
+ // If no events are selected, then we don't have any to drag
+ return null;
+ }
+ else if (!allUserEditable(timelineEventDragEvents)) {
+ // If any selected event is not user-editable, don't allow editing
+ return 'undraggable';
+ }
+ else if (timelineEventDragEvents.length > 1) {
+ // If more than one event is selected, don't allow edge dragging
+ return 'center';
+ }
+ else if (timelineEventDragEvents.length === 1) {
+ const event = timelineEventDragEvents[0];
+ const pxPerMilli = 1 / timelineUi.millisPerPx.value;
+ const eventWidth = (event.end_PMILLIS - event.start_PMILLIS) * pxPerMilli;
+ if (eventWidth < minEventWidthForEdgeDraggability) {
+ // If event isn't very wide, don't try to allow edge dragging
return 'center';
}
- else if ( eventDragEvents.length === 1 ) {
- var event = eventDragEvents[ 0 ];
- var pxPerMilli = 1 / ui.millisPerPx.value;
- var eventWidth = ( event.end_PMILLIS - event.start_PMILLIS ) * pxPerMilli;
- if ( eventWidth < minEventWidthForEdgeDraggability ) {
- // If event isn't very wide, don't try to allow edge dragging
+ else {
+ const mouseOffset = (mouseTime_PMILLIS - event.start_PMILLIS) * pxPerMilli;
+ if (mouseOffset < draggableEdgeWidth) {
+ // If mouse is near the left edge, drag the event's start-time
+ return 'start';
+ }
+ else if (mouseOffset < eventWidth - draggableEdgeWidth) {
+ // If mouse is in the center, drag the whole event
return 'center';
}
else {
- var mouseOffset = ( mouseTime_PMILLIS - event.start_PMILLIS ) * pxPerMilli;
- if ( mouseOffset < draggableEdgeWidth ) {
- // If mouse is near the left edge, drag the event's start-time
- return 'start';
- }
- else if ( mouseOffset < eventWidth - draggableEdgeWidth ) {
- // If mouse is in the center, drag the whole event
- return 'center';
- }
- else {
- // If mouse is near the right edge, drag the event's end-time
- return 'end';
- }
+ // If mouse is near the right edge, drag the event's end-time
+ return 'end';
}
}
- else {
- // Should never get here, because we have clauses above for length === 0, length === 1, and length > 1
- return null;
- }
}
+ else {
+ // Should never get here, because we have clauses above for length === 0, length === 1, and length > 1
+ return null;
+ }
+ }
- var updateCursor = function( ) {
- if ( !eventDragMode ) {
+ const updateCursor = function () {
+ if (!eventDragMode) {
- var mouseCursors = { 'center': 'default', 'start': 'w-resize', 'end': 'e-resize', 'undraggable': 'default' };
- var hoveredTime_PMILLIS = selection.hoveredTime_PMILLIS.value;
-
- // if a multi-selection has been made, update the cursor based on all the events in the multi-selection
- if ( selection.selectedEvents.length > 1 ) {
- rowContentPane.mouseCursor = mouseCursors[ chooseEventDragMode( ui, hoveredTime_PMILLIS, selection.selectedEvents.toArray( ) ) ];
- }
- else {
- var hoveredEvent = selection.hoveredEvent.value;
- var hoveredEvents = ( hasval( hoveredEvent ) ? [ hoveredEvent ] : [] );
- rowContentPane.mouseCursor = mouseCursors[ chooseEventDragMode( ui, hoveredTime_PMILLIS, hoveredEvents ) ];
- }
+ const mouseCursors = { 'center': 'default', 'start': 'w-resize', 'end': 'e-resize', 'undraggable': 'default' };
+ const hoveredTime_PMILLIS = selection.hoveredTime_PMILLIS.value;
+
+ // if a multi-selection has been made, update the cursor based on all the events in the multi-selection
+ if (selection.selectedEvents.length > 1) {
+ rowContentPane.mouseCursor = mouseCursors[chooseEventDragMode(ui, hoveredTime_PMILLIS, selection.selectedEvents.toArray())];
}
- };
- ui.millisPerPx.changed.on( updateCursor );
- selection.hoveredTime_PMILLIS.changed.on( updateCursor );
- selection.hoveredEvent.changed.on( updateCursor );
-
- rowContentPane.mouseDown.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) ) {
- var eventDragEventsSet = selection.selectedEvents;
- eventDragEvents = eventDragEventsSet.toArray( );
- eventDragMode = chooseEventDragMode( ui, timeAtPointer_PMILLIS( ev ), eventDragEvents );
-
- eventDragSnapTimes_PMILLIS = new Array( );
- var numSnapTimes = 0;
- var allEventGuids = row.eventGuids;
- for ( var n = 0; n < allEventGuids.length; n++ ) {
- var eventGuid = allEventGuids.valueAt( n );
- if ( !eventDragEventsSet.hasId( eventGuid ) ) {
- var event = model.event( eventGuid );
- eventDragSnapTimes_PMILLIS.push( event.start_PMILLIS );
- eventDragSnapTimes_PMILLIS.push( event.end_PMILLIS );
- }
- }
- eventDragSnapTimes_PMILLIS.sort( );
+ else {
+ const hoveredEvent = selection.hoveredEvent.value;
+ const hoveredEvents = (hasval(hoveredEvent) ? [hoveredEvent] : []);
+ rowContentPane.mouseCursor = mouseCursors[chooseEventDragMode(ui, hoveredTime_PMILLIS, hoveredEvents)];
}
- } );
-
- function findSnapShift_MILLIS( t_PMILLIS : number, maxShift_MILLIS : number ) : number {
- var i = indexNearest( eventDragSnapTimes_PMILLIS, t_PMILLIS );
- if ( i >= 0 )
- {
- var shift_MILLIS = eventDragSnapTimes_PMILLIS[ i ] - t_PMILLIS;
- if ( Math.abs( shift_MILLIS ) <= maxShift_MILLIS ) {
- return shift_MILLIS;
+ }
+ };
+ ui.millisPerPx.changed.on(updateCursor);
+ selection.hoveredTime_PMILLIS.changed.on(updateCursor);
+ selection.hoveredEvent.changed.on(updateCursor);
+
+ rowContentPane.mouseDown.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent)) {
+ const eventDragEventsSet = selection.selectedEvents;
+ eventDragEvents = eventDragEventsSet.toArray();
+ eventDragMode = chooseEventDragMode(ui, timeAtPointer_PMILLIS(ev), eventDragEvents);
+
+ eventDragSnapTimes_PMILLIS = new Array();
+ const numSnapTimes = 0;
+ const allEventGuids = row.eventGuids;
+ for (let n = 0; n < allEventGuids.length; n++) {
+ const eventGuid = allEventGuids.valueAt(n);
+ if (!eventDragEventsSet.hasId(eventGuid)) {
+ const event = model.event(eventGuid);
+ eventDragSnapTimes_PMILLIS.push(event.start_PMILLIS);
+ eventDragSnapTimes_PMILLIS.push(event.end_PMILLIS);
}
}
- return null;
+ eventDragSnapTimes_PMILLIS.sort();
}
+ });
+
+ function findSnapShift_MILLIS(t_PMILLIS: number, maxShift_MILLIS: number): number {
+ const i = indexNearest(eventDragSnapTimes_PMILLIS, t_PMILLIS);
+ if (i >= 0) {
+ const shift_MILLIS = eventDragSnapTimes_PMILLIS[i] - t_PMILLIS;
+ if (Math.abs(shift_MILLIS) <= maxShift_MILLIS) {
+ return shift_MILLIS;
+ }
+ }
+ return null;
+ }
- // Compute (and remember) the pointer time, for use by the event-drag listeners below
- //
-
- var eventDragPointer_PMILLIS : number = null;
+ // Compute (and remember) the pointer time, for use by the event-drag listeners below
+ //
- var updateEventDragPointer = function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) && eventDragMode ) {
- eventDragPointer_PMILLIS = timeAtPointer_PMILLIS( ev );
- }
- };
- rowContentPane.mouseDown.on( updateEventDragPointer );
- rowContentPane.mouseMove.on( updateEventDragPointer );
+ let eventDragPointer_PMILLIS: number = null;
+ const updateEventDragPointer = function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent) && eventDragMode) {
+ eventDragPointer_PMILLIS = timeAtPointer_PMILLIS(ev);
+ }
+ };
+ rowContentPane.mouseDown.on(updateEventDragPointer);
+ rowContentPane.mouseMove.on(updateEventDragPointer);
- // Dragging event-center
- //
- var grabEventCenter = function( ) {
- if ( eventDragMode === 'center' ) {
- for ( var n = 0; n < eventDragEvents.length; n++ ) {
- var event = eventDragEvents[ n ];
- eventDragOffsets_MILLIS[ event.eventGuid ] = eventDragPointer_PMILLIS - event.start_PMILLIS;
- }
+ // Dragging event-center
+ //
- // If this is a simple click-and-release, leave the mouse-cursor alone --
- // but once we can tell that it's actually a drag, change to a drag cursor
- //
-
- var beginDrag = function( ) {
- rowContentPane.mouseCursor = 'move';
- };
- rowContentPane.mouseMove.on( beginDrag );
- var pendingBeginDrag = setTimeout( beginDrag, 300 );
-
- var endDrag = function( ) {
- clearTimeout( pendingBeginDrag );
- rowContentPane.mouseMove.off( beginDrag );
- rowContentPane.mouseUp.off( endDrag );
- };
- rowContentPane.mouseUp.on( endDrag );
+ const grabEventCenter = function () {
+ if (eventDragMode === 'center') {
+ for (let n = 0; n < eventDragEvents.length; n++) {
+ const event = eventDragEvents[n];
+ eventDragOffsets_MILLIS[event.eventGuid] = eventDragPointer_PMILLIS - event.start_PMILLIS;
}
- };
- rowContentPane.mouseDown.on( grabEventCenter );
-
- var dragEventCenter = function( ) {
- if ( eventDragMode === 'center' ) {
- var maxSnapShift_MILLIS = snapToDistance * ( timeAxis.tSize_MILLIS / rowContentPane.viewport.w );
-
- var snapShift_MILLIS : number = null;
- for ( var n = 0; n < eventDragEvents.length; n++ ) {
- var event = eventDragEvents[ n ];
- var newStart_PMILLIS = ( eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[ event.eventGuid ] );
- var newEnd_PMILLIS = event.end_PMILLIS + ( newStart_PMILLIS - event.start_PMILLIS );
-
- var eventStartSnapShift_MILLIS = findSnapShift_MILLIS( newStart_PMILLIS, maxSnapShift_MILLIS );
- if ( hasval( eventStartSnapShift_MILLIS ) ) {
- if ( !hasval( snapShift_MILLIS ) || Math.abs( eventStartSnapShift_MILLIS ) < Math.abs( snapShift_MILLIS )) {
- snapShift_MILLIS = eventStartSnapShift_MILLIS;
- }
- }
- var eventEndSnapShift_MILLIS = findSnapShift_MILLIS( newEnd_PMILLIS, maxSnapShift_MILLIS );
- if ( hasval( eventEndSnapShift_MILLIS ) ) {
- if ( !hasval( snapShift_MILLIS ) || Math.abs( eventEndSnapShift_MILLIS ) < Math.abs( snapShift_MILLIS )) {
- snapShift_MILLIS = eventEndSnapShift_MILLIS;
- }
+ // If this is a simple click-and-release, leave the mouse-cursor alone --
+ // but once we can tell that it's actually a drag, change to a drag cursor
+ //
+
+ const beginDrag = function () {
+ rowContentPane.mouseCursor = 'move';
+ };
+ rowContentPane.mouseMove.on(beginDrag);
+ const pendingBeginDrag = setTimeout(beginDrag, 300);
+
+ const endDrag = function () {
+ clearTimeout(pendingBeginDrag);
+ rowContentPane.mouseMove.off(beginDrag);
+ rowContentPane.mouseUp.off(endDrag);
+ };
+ rowContentPane.mouseUp.on(endDrag);
+ }
+ };
+ rowContentPane.mouseDown.on(grabEventCenter);
+
+ const dragEventCenter = function () {
+ if (eventDragMode === 'center') {
+ const maxSnapShift_MILLIS = snapToDistance * (timeAxis.tSize_MILLIS / rowContentPane.viewport.w);
+
+ let snapShift_MILLIS: number = null;
+ for (let n = 0; n < eventDragEvents.length; n++) {
+ const event = eventDragEvents[n];
+ const newStart_PMILLIS = (eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[event.eventGuid]);
+ const newEnd_PMILLIS = event.end_PMILLIS + (newStart_PMILLIS - event.start_PMILLIS);
+
+ const eventStartSnapShift_MILLIS = findSnapShift_MILLIS(newStart_PMILLIS, maxSnapShift_MILLIS);
+ if (hasval(eventStartSnapShift_MILLIS)) {
+ if (!hasval(snapShift_MILLIS) || Math.abs(eventStartSnapShift_MILLIS) < Math.abs(snapShift_MILLIS)) {
+ snapShift_MILLIS = eventStartSnapShift_MILLIS;
}
}
- if ( !hasval( snapShift_MILLIS ) ) {
- snapShift_MILLIS = 0;
- }
- for ( var n = 0; n < eventDragEvents.length; n++ ) {
- var event = eventDragEvents[ n ];
- var newStart_PMILLIS = eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[ event.eventGuid ] + snapShift_MILLIS;
- var newEnd_PMILLIS = event.end_PMILLIS + ( newStart_PMILLIS - event.start_PMILLIS );
- event.setInterval( newStart_PMILLIS, newEnd_PMILLIS );
+ const eventEndSnapShift_MILLIS = findSnapShift_MILLIS(newEnd_PMILLIS, maxSnapShift_MILLIS);
+ if (hasval(eventEndSnapShift_MILLIS)) {
+ if (!hasval(snapShift_MILLIS) || Math.abs(eventEndSnapShift_MILLIS) < Math.abs(snapShift_MILLIS)) {
+ snapShift_MILLIS = eventEndSnapShift_MILLIS;
+ }
}
}
- };
- rowContentPane.mouseMove.on( dragEventCenter );
-
-
- // Dragging event-start
- //
-
- var grabEventStart = function( ) {
- if ( eventDragMode === 'start' ) {
- for ( var n = 0; n < eventDragEvents.length; n++ ) {
- var event = eventDragEvents[ n ];
- eventDragOffsets_MILLIS[ event.eventGuid ] = eventDragPointer_PMILLIS - event.start_PMILLIS;
- }
+ if (!hasval(snapShift_MILLIS)) {
+ snapShift_MILLIS = 0;
}
- };
- rowContentPane.mouseDown.on( grabEventStart );
-
- var dragEventStart = function( ) {
- if ( eventDragMode === 'start' ) {
- var wMin_MILLIS = minEventWidthWhenDraggingEdge * timeAxis.vSize / rowContentPane.viewport.w;
- var maxSnapShift_MILLIS = snapToDistance * ( timeAxis.tSize_MILLIS / rowContentPane.viewport.w );
-
- var snapShift_MILLIS : number = null;
- for ( var n = 0; n < eventDragEvents.length; n++ ) {
- var event = eventDragEvents[ n ];
- var newStart_PMILLIS = eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[ event.eventGuid ];
-
- var eventSnapShift_MILLIS = findSnapShift_MILLIS( newStart_PMILLIS, maxSnapShift_MILLIS );
- if ( hasval( eventSnapShift_MILLIS ) ) {
- if ( !hasval( snapShift_MILLIS ) || Math.abs( eventSnapShift_MILLIS ) < Math.abs( snapShift_MILLIS )) {
- snapShift_MILLIS = eventSnapShift_MILLIS;
- }
- }
- }
- if ( !hasval( snapShift_MILLIS ) ) {
- snapShift_MILLIS = 0;
- }
- for ( var n = 0; n < eventDragEvents.length; n++ ) {
- var event = eventDragEvents[ n ];
- var newStart_PMILLIS = eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[ event.eventGuid ] + snapShift_MILLIS;
- event.start_PMILLIS = Math.min( event.end_PMILLIS - wMin_MILLIS, newStart_PMILLIS );
- }
+ for (let n = 0; n < eventDragEvents.length; n++) {
+ const event = eventDragEvents[n];
+ const newStart_PMILLIS = eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[event.eventGuid] + snapShift_MILLIS;
+ const newEnd_PMILLIS = event.end_PMILLIS + (newStart_PMILLIS - event.start_PMILLIS);
+ event.setInterval(newStart_PMILLIS, newEnd_PMILLIS);
}
- };
- rowContentPane.mouseMove.on( dragEventStart );
- timeAxis.limitsChanged.on( dragEventStart );
+ }
+ };
+ rowContentPane.mouseMove.on(dragEventCenter);
- // Dragging event-end
- //
+ // Dragging event-start
+ //
- var grabEventEnd = function( ) {
- if ( eventDragMode === 'end' ) {
- for ( var n = 0; n < eventDragEvents.length; n++ ) {
- var event = eventDragEvents[ n ];
- eventDragOffsets_MILLIS[ event.eventGuid ] = eventDragPointer_PMILLIS - event.end_PMILLIS;
- }
+ const grabEventStart = function () {
+ if (eventDragMode === 'start') {
+ for (let n = 0; n < eventDragEvents.length; n++) {
+ const event = eventDragEvents[n];
+ eventDragOffsets_MILLIS[event.eventGuid] = eventDragPointer_PMILLIS - event.start_PMILLIS;
}
- };
- rowContentPane.mouseDown.on( grabEventEnd );
-
- var dragEventEnd = function( ) {
- if ( eventDragMode === 'end' ) {
- var wMin_MILLIS = minEventWidthWhenDraggingEdge * timeAxis.vSize / rowContentPane.viewport.w;
- var maxSnapShift_MILLIS = snapToDistance * ( timeAxis.tSize_MILLIS / rowContentPane.viewport.w );
-
- var snapShift_MILLIS : number = null;
- for ( var n = 0; n < eventDragEvents.length; n++ ) {
- var event = eventDragEvents[ n ];
- var newEnd_PMILLIS = eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[ event.eventGuid ];
-
- var eventSnapShift_MILLIS = findSnapShift_MILLIS( newEnd_PMILLIS, maxSnapShift_MILLIS );
- if ( hasval( eventSnapShift_MILLIS ) ) {
- if ( !hasval( snapShift_MILLIS ) || Math.abs( eventSnapShift_MILLIS ) < Math.abs( snapShift_MILLIS )) {
- snapShift_MILLIS = eventSnapShift_MILLIS;
- }
+ }
+ };
+ rowContentPane.mouseDown.on(grabEventStart);
+
+ const dragEventStart = function () {
+ if (eventDragMode === 'start') {
+ const wMin_MILLIS = minEventWidthWhenDraggingEdge * timeAxis.vSize / rowContentPane.viewport.w;
+ const maxSnapShift_MILLIS = snapToDistance * (timeAxis.tSize_MILLIS / rowContentPane.viewport.w);
+
+ let snapShift_MILLIS: number = null;
+ for (let n = 0; n < eventDragEvents.length; n++) {
+ const event = eventDragEvents[n];
+ const newStart_PMILLIS = eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[event.eventGuid];
+
+ const eventSnapShift_MILLIS = findSnapShift_MILLIS(newStart_PMILLIS, maxSnapShift_MILLIS);
+ if (hasval(eventSnapShift_MILLIS)) {
+ if (!hasval(snapShift_MILLIS) || Math.abs(eventSnapShift_MILLIS) < Math.abs(snapShift_MILLIS)) {
+ snapShift_MILLIS = eventSnapShift_MILLIS;
}
}
- if ( !hasval( snapShift_MILLIS ) ) {
- snapShift_MILLIS = 0;
- }
+ }
+ if (!hasval(snapShift_MILLIS)) {
+ snapShift_MILLIS = 0;
+ }
- for ( var n = 0; n < eventDragEvents.length; n++ ) {
- var event = eventDragEvents[ n ];
- var newEnd_PMILLIS = eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[ event.eventGuid ] + snapShift_MILLIS;
- event.end_PMILLIS = Math.max( event.start_PMILLIS + wMin_MILLIS, newEnd_PMILLIS );
- }
+ for (let n = 0; n < eventDragEvents.length; n++) {
+ const event = eventDragEvents[n];
+ const newStart_PMILLIS = eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[event.eventGuid] + snapShift_MILLIS;
+ event.start_PMILLIS = Math.trunc(Math.min(event.end_PMILLIS - wMin_MILLIS, newStart_PMILLIS));
}
- };
- rowContentPane.mouseMove.on( dragEventEnd );
- timeAxis.limitsChanged.on( dragEventEnd );
-
-
- // Finish event-drag
- //
-
- rowContentPane.mouseUp.on( function( ev : PointerEvent ) {
- eventDragEvents = [];
- eventDragOffsets_MILLIS = {};
- eventDragSnapTimes_PMILLIS = [];
- eventDragPointer_PMILLIS = null;
- eventDragMode = null;
- } );
-
-
- rowContentPane.dispose.on( function( ) {
- lanes.dispose( );
-
- timeAxis.limitsChanged.off( dragEventEnd );
- timeAxis.limitsChanged.off( dragEventStart );
-
- ui.millisPerPx.changed.off( uiMillisPerPxChanged );
-
- ui.millisPerPx.changed.off( updateCursor );
- selection.hoveredTime_PMILLIS.changed.off( updateCursor );
- selection.hoveredEvent.changed.off( updateCursor );
-
- row.eventGuids.valueAdded.off( redraw );
- row.eventGuids.valueMoved.off( redraw );
- row.eventGuids.valueRemoved.off( redraw );
- row.eventGuids.valueRemoved.off( removeRedraw );
-
- row.eventGuids.valueAdded.off( watchEventAttrs );
-
- row.eventGuids.forEach( function( eventGuid : string ) {
- model.event( eventGuid ).attrsChanged.off( redraw );
- } );
- } );
-
- return rowContentPane;
+ }
};
- }
+ rowContentPane.mouseMove.on(dragEventStart);
+ timeAxis.limitsChanged.on(dragEventStart);
- export interface TimelineEventLimitsPainterOptions {
- lineColor? : Color;
- lineThickness? : number;
- }
- function eventLimitsPainterHelper( limitsOpts : TimelineEventLimitsPainterOptions, drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) {
-
- var rowTopPadding = options.rowTopPadding;
- var rowBottomPadding = options.rowBottomPadding;
- var laneHeight = options.laneHeight;
-
- var lineColor = ( hasval( limitsOpts ) && hasval( limitsOpts.lineColor ) ? limitsOpts.lineColor : new Color( 1, 0, 0, 1 ) );
- var lineThickness = ( hasval( limitsOpts ) && hasval( limitsOpts.lineThickness ) ? limitsOpts.lineThickness : 2.5 );
-
- var xyFrac_vColor_VERTSHADER = concatLines(
- ' ',
- ' attribute vec2 a_XyFrac; ',
- ' attribute vec4 a_Color; ',
- ' ',
- ' varying vec4 v_Color; ',
- ' ',
- ' void main( ) { ',
- ' gl_Position = vec4( ( -1.0 + 2.0*a_XyFrac ), 0.0, 1.0 ); ',
- ' v_Color = a_Color; ',
- ' } ',
- ' '
- );
-
- var program = new Program( xyFrac_vColor_VERTSHADER, varyingColor_FRAGSHADER );
- var a_XyFrac = new Attribute( program, 'a_XyFrac' );
- var a_Color = new Attribute( program, 'a_Color' );
-
- var xys = new Float32Array( 0 );
- var xysBuffer = newDynamicBuffer( );
-
- var rgbas = new Float32Array( 0 );
- var rgbasBuffer = newDynamicBuffer( );
-
- return {
- paint( indexXys : number, indexRgbas : number, gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- if ( indexXys > 0 ) {
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
-
- program.use( gl );
- xysBuffer.setData( xys.subarray( 0, indexXys ) );
- a_XyFrac.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
- rgbasBuffer.setData( rgbas.subarray( 0, indexRgbas ) );
- a_Color.setDataAndEnable( gl, rgbasBuffer, 4, GL.FLOAT );
-
- gl.drawArrays( GL.TRIANGLES, 0, Math.floor( indexXys / 2 ) );
-
- a_Color.disable( gl );
- a_XyFrac.disable( gl );
- program.endUse( gl );
+ // Dragging event-end
+ //
+
+ const grabEventEnd = function () {
+ if (eventDragMode === 'end') {
+ for (let n = 0; n < eventDragEvents.length; n++) {
+ const event = eventDragEvents[n];
+ eventDragOffsets_MILLIS[event.eventGuid] = eventDragPointer_PMILLIS - event.end_PMILLIS;
}
- },
- ensureCapacity: function( eventCount : number ) {
- var numVertices = ( 6 * 3 /* triangles */ * eventCount );
- xys = ensureCapacityFloat32( xys, 2*numVertices );
- rgbas = ensureCapacityFloat32( rgbas, 4*numVertices );
- },
- fillEvent: function( laneIndex : number, eventIndex : number, indexXys : number, indexRgbas : number, viewport : BoundsUnmodifiable ) : { indexXys : number; indexRgbas : number } {
-
- var lane : TimelineLane = lanes.lane( laneIndex );
- var event : TimelineEventModel = lane.event( eventIndex );
-
- var wLine = lineThickness / viewport.w;
- var hLine = lineThickness / viewport.h;
-
- var jTop = rowTopPadding + ( laneIndex )*laneHeight;
- var yTop = ( viewport.h - jTop ) / viewport.h;
- var jBottom = rowTopPadding + ( laneIndex + 1 )*laneHeight;
- var yBottom = ( viewport.h - jBottom ) / viewport.h;
- var yMid = ( yTop + yBottom ) / 2;
-
- var xLeft = hasval( event.startLimit_PMILLIS ) ? timeAxis.tFrac( event.startLimit_PMILLIS ) : 0;
- var xRight = hasval( event.endLimit_PMILLIS ) ? timeAxis.tFrac( event.endLimit_PMILLIS ) : 1;
-
- indexXys = putQuadXys( xys, indexXys, xLeft, xRight, yMid-hLine/2, yMid+hLine/2 );
- indexXys = putQuadXys( xys, indexXys, xLeft, xLeft-wLine, yTop, yBottom );
- indexXys = putQuadXys( xys, indexXys, xRight, xRight+wLine, yTop, yBottom );
- indexRgbas = putRgbas( rgbas, indexRgbas, lineColor, 18 );
-
- return { indexXys : indexXys, indexRgbas : indexRgbas };
}
};
- }
-
- export function newEventLimitsPainterFactory( limitOpts? : TimelineEventLimitsPainterOptions ) : TimelineEventsPainterFactory {
-
- // Painter Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) : Painter {
-
- var helper = eventLimitsPainterHelper( limitOpts, drawable, timeAxis, lanes, ui, options );
-
- // Painter
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
-
- var selectedEvents : OrderedSet = ui.selection.selectedEvents;
-
- //XXX Instead of estimating the number of events we will need to draw ahead of time
- //XXX (difficult because selected events may be present in multiple lanes, so
- //XXX selectedEvents.length might not be sufficient) just make enough space for all events.
- //XXX Potentially quite inefficient with lots of events (and few selected events).
- helper.ensureCapacity( lanes.numEvents );
-
- var indexXys = 0;
- var indexRgbas = 0;
-
- for ( var l = 0; l < lanes.length; l++ ) {
- var lane = lanes.lane( l );
- for ( var e = 0; e < lane.length; e++ ) {
- var event = lane.event( e );
-
- // check whether the event is selected and has limits defined
- if ( selectedEvents.hasId( event.eventGuid ) && ( hasval( event.startLimit_PMILLIS ) || hasval( event.endLimit_PMILLIS ) ) ) {
- var indexes = helper.fillEvent( l, e, indexXys, indexRgbas, viewport );
- indexXys = indexes.indexXys;
- indexRgbas = indexes.indexRgbas;
+ rowContentPane.mouseDown.on(grabEventEnd);
+
+ const dragEventEnd = function () {
+ if (eventDragMode === 'end') {
+ const wMin_MILLIS = minEventWidthWhenDraggingEdge * timeAxis.vSize / rowContentPane.viewport.w;
+ const maxSnapShift_MILLIS = snapToDistance * (timeAxis.tSize_MILLIS / rowContentPane.viewport.w);
+
+ let snapShift_MILLIS: number = null;
+ for (let n = 0; n < eventDragEvents.length; n++) {
+ const event = eventDragEvents[n];
+ const newEnd_PMILLIS = eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[event.eventGuid];
+
+ const eventSnapShift_MILLIS = findSnapShift_MILLIS(newEnd_PMILLIS, maxSnapShift_MILLIS);
+ if (hasval(eventSnapShift_MILLIS)) {
+ if (!hasval(snapShift_MILLIS) || Math.abs(eventSnapShift_MILLIS) < Math.abs(snapShift_MILLIS)) {
+ snapShift_MILLIS = eventSnapShift_MILLIS;
}
}
}
-
- helper.paint( indexXys, indexRgbas, gl, viewport );
- };
+ if (!hasval(snapShift_MILLIS)) {
+ snapShift_MILLIS = 0;
+ }
+
+ for (let n = 0; n < eventDragEvents.length; n++) {
+ const event = eventDragEvents[n];
+ const newEnd_PMILLIS = eventDragPointer_PMILLIS - eventDragOffsets_MILLIS[event.eventGuid] + snapShift_MILLIS;
+ event.end_PMILLIS = Math.trunc(Math.max(event.start_PMILLIS + wMin_MILLIS, newEnd_PMILLIS));
+ }
+ }
};
- }
+ rowContentPane.mouseMove.on(dragEventEnd);
+ timeAxis.limitsChanged.on(dragEventEnd);
- export enum JointType {
- BEVEL, MITER
- }
-
- export interface TimelineEventBarsPainterOptions {
- topMargin? : number;
- bottomMargin? : number;
- borderThickness? : number;
- cornerType? : JointType;
- defaultColor? : Color;
- defaultBorderColor? : Color;
- selectedBorderColor? : Color;
-
- // the size of the dashes when painting stippled event borders
- dashLength? : number;
- // the width of stripes in the painter fill
- stripeWidth? : number;
- // the width of secondary color stripes in the painter fill
- stripeSecondaryWidth? : number;
- // the slant of the stipes: 0 = horizontal, 1 = 45 degrees, -1 = 45 degrees backward
- stripeSlant? : number;
- // width in pixels of the antialiasing of the slant
- featherWidth? : number;
-
- // minimum pixel width of the event bar
- // when the timeline is zoomed out so that the event bar
- // is smaller than this visible width, the event bar is hidden
- minimumVisibleWidth? : number;
- }
-
- export enum FillPattern {
- solid = 0,
- stripe = 1,
- gradient = 2
- }
+ // Finish event-drag
+ //
+
+ rowContentPane.mouseUp.on(function (ev: PointerEvent) {
+ eventDragEvents = [];
+ eventDragOffsets_MILLIS = {};
+ eventDragSnapTimes_PMILLIS = [];
+ eventDragPointer_PMILLIS = null;
+ eventDragMode = null;
+ });
+
+
+ rowContentPane.dispose.on(function () {
+ lanes.dispose();
+
+ timeAxis.limitsChanged.off(dragEventEnd);
+ timeAxis.limitsChanged.off(dragEventStart);
+
+ ui.millisPerPx.changed.off(uiMillisPerPxChanged);
+
+ ui.millisPerPx.changed.off(updateCursor);
+ selection.hoveredTime_PMILLIS.changed.off(updateCursor);
+ selection.hoveredEvent.changed.off(updateCursor);
+
+ row.eventGuids.valueAdded.off(redraw);
+ row.eventGuids.valueMoved.off(redraw);
+ row.eventGuids.valueRemoved.off(redraw);
+ row.eventGuids.valueRemoved.off(removeRedraw);
+
+ row.eventGuids.valueAdded.off(watchEventAttrs);
+
+ row.eventGuids.forEach(function (eventGuid: string) {
+ model.event(eventGuid).attrsChanged.off(redraw);
+ });
+ });
+
+ return rowContentPane;
+ };
+}
+
+export interface TimelineEventLimitsPainterOptions {
+ lineColor?: Color;
+ lineThickness?: number;
+}
+
+function eventLimitsPainterHelper(limitsOpts: TimelineEventLimitsPainterOptions, drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions) {
+
+ const rowTopPadding = options.rowTopPadding;
+ const rowBottomPadding = options.rowBottomPadding;
+ const laneHeight = options.laneHeight;
+
+ const lineColor = (hasval(limitsOpts) && hasval(limitsOpts.lineColor) ? limitsOpts.lineColor : new Color(1, 0, 0, 1));
+ const lineThickness = (hasval(limitsOpts) && hasval(limitsOpts.lineThickness) ? limitsOpts.lineThickness : 2.5);
+
+ const xyFrac_vColor_VERTSHADER = concatLines(
+ ' ',
+ ' attribute vec2 a_XyFrac; ',
+ ' attribute vec4 a_Color; ',
+ ' ',
+ ' varying vec4 v_Color; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_Position = vec4( ( -1.0 + 2.0*a_XyFrac ), 0.0, 1.0 ); ',
+ ' v_Color = a_Color; ',
+ ' } ',
+ ' '
+ );
+
+ const program = new Program(xyFrac_vColor_VERTSHADER, varyingColor_FRAGSHADER);
+ const a_XyFrac = new Attribute(program, 'a_XyFrac');
+ const a_Color = new Attribute(program, 'a_Color');
+
+ let xys = new Float32Array(0);
+ const xysBuffer = newDynamicBuffer();
+
+ let rgbas = new Float32Array(0);
+ const rgbasBuffer = newDynamicBuffer();
+
+ return {
+ paint(indexXys: number, indexRgbas: number, gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ if (indexXys > 0) {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ program.use(gl);
+ xysBuffer.setData(xys.subarray(0, indexXys));
+ a_XyFrac.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+ rgbasBuffer.setData(rgbas.subarray(0, indexRgbas));
+ a_Color.setDataAndEnable(gl, rgbasBuffer, 4, GL.FLOAT);
+
+ gl.drawArrays(GL.TRIANGLES, 0, Math.floor(indexXys / 2));
+
+ a_Color.disable(gl);
+ a_XyFrac.disable(gl);
+ program.endUse(gl);
+ }
+ },
+ ensureCapacity: function (eventCount: number) {
+ const numVertices = (6 * 3 /* triangles */ * eventCount);
+ xys = ensureCapacityFloat32(xys, 2 * numVertices);
+ rgbas = ensureCapacityFloat32(rgbas, 4 * numVertices);
+ },
+ fillEvent: function (laneIndex: number, eventIndex: number, indexXys: number, indexRgbas: number, viewport: BoundsUnmodifiable): { indexXys: number; indexRgbas: number } {
+
+ const lane: TimelineLane = lanes.lane(laneIndex);
+ const event: TimelineEventModel = lane.event(eventIndex);
+
+ const wLine = lineThickness / viewport.w;
+ const hLine = lineThickness / viewport.h;
+
+ const jTop = rowTopPadding + (laneIndex) * laneHeight;
+ const yTop = (viewport.h - jTop) / viewport.h;
+ const jBottom = rowTopPadding + (laneIndex + 1) * laneHeight;
+ const yBottom = (viewport.h - jBottom) / viewport.h;
+ const yMid = (yTop + yBottom) / 2;
+
+ const xLeft = hasval(event.startLimit_PMILLIS) ? timeAxis.tFrac(event.startLimit_PMILLIS) : 0;
+ const xRight = hasval(event.endLimit_PMILLIS) ? timeAxis.tFrac(event.endLimit_PMILLIS) : 1;
+
+ indexXys = putQuadXys(xys, indexXys, xLeft, xRight, yMid - hLine / 2, yMid + hLine / 2);
+ indexXys = putQuadXys(xys, indexXys, xLeft, xLeft - wLine, yTop, yBottom);
+ indexXys = putQuadXys(xys, indexXys, xRight, xRight + wLine, yTop, yBottom);
+ indexRgbas = putRgbas(rgbas, indexRgbas, lineColor, 18);
+
+ return { indexXys: indexXys, indexRgbas: indexRgbas };
+ }
+ };
+}
+
+export function newEventLimitsPainterFactory(limitOpts?: TimelineEventLimitsPainterOptions): TimelineEventsPainterFactory {
+
+ // Painter Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions): Painter {
+
+ const helper = eventLimitsPainterHelper(limitOpts, drawable, timeAxis, lanes, ui, options);
+
+ // Painter
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
- function eventStripedBarPainterHelper( barOpts : TimelineEventBarsPainterOptions, drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) {
- var rowTopPadding = options.rowTopPadding;
- var rowBottomPadding = options.rowBottomPadding;
- var laneHeight = options.laneHeight;
-
- var topMargin = ( hasval( barOpts ) && hasval( barOpts.topMargin ) ? barOpts.topMargin : 1.2 );
- var bottomMargin = ( hasval( barOpts ) && hasval( barOpts.bottomMargin ) ? barOpts.bottomMargin : 1.2 );
- var borderThickness = ( hasval( barOpts ) && hasval( barOpts.borderThickness ) ? barOpts.borderThickness : 2 );
- var cornerType = ( hasval( barOpts ) && hasval( barOpts.cornerType ) ? barOpts.cornerType : JointType.BEVEL );
- var defaultColor = ( hasval( barOpts ) && hasval( barOpts.defaultColor ) ? barOpts.defaultColor : options.timelineFgColor.withAlphaTimes( 0.4 ) );
- var defaultColorSecondary = new Color( 1, 1, 1, 1 );
- var minimumVisibleWidth = ( hasval( barOpts ) && hasval( barOpts.minimumVisibleWidth ) ? barOpts.minimumVisibleWidth : 0 );
-
- var stripeWidth = ( hasval( barOpts ) && hasval( barOpts.stripeWidth ) ? barOpts.stripeWidth : 5 );
- var stripeSecondaryWidth= ( hasval( barOpts ) && hasval( barOpts.stripeSecondaryWidth ) ? barOpts.stripeSecondaryWidth : 5 );
- var stripeSlant = ( hasval( barOpts ) && hasval( barOpts.stripeSlant ) ? barOpts.stripeSlant : 1 );
- var featherWidth = ( hasval( barOpts ) && hasval( barOpts.featherWidth ) ? barOpts.featherWidth : 2 );
-
- var selection = ui.selection;
-
- var xyFrac_vColor_VERTSHADER = concatLines(
- ' ',
- ' attribute vec2 a_XyFrac; ',
- ' attribute vec4 a_Color; ',
- ' attribute vec4 a_ColorSecondary; ',
- ' attribute vec2 a_relativeXy; ',
- ' attribute float a_fillPattern; ',
- ' ',
- ' varying vec4 v_Color; ',
- ' varying vec4 v_ColorSecondary; ',
- ' varying vec2 v_relativeXy; ',
- ' varying float v_fillPattern; ',
- ' ',
- ' void main( ) { ',
- ' gl_Position = vec4( ( -1.0 + 2.0*a_XyFrac ), 0.0, 1.0 ); ',
- ' v_Color = a_Color; ',
- ' v_ColorSecondary = a_ColorSecondary; ',
- ' v_relativeXy = a_relativeXy; ',
- ' v_fillPattern = a_fillPattern; ',
- ' } ',
- ' '
- );
-
- var fillPattern_FRAGSHADER = Webglimpse.concatLines(
- ' #define PI 3.1415926535897932384626433832795 ',
- ' ',
- ' precision lowp float; ',
- ' // the width in pixels of the first color stripe ',
- ' uniform float u_stripeWidth; ',
- ' // the width in pixels of the second color stripe ',
- ' uniform float u_stripeSecondaryWidth; ',
- ' // the slant of the stipes: 0 = horizontal, 1 = 45 degrees ',
- ' uniform float u_slant; ',
- ' // width in pixels of the antialiasing of the slant ',
- ' uniform float u_featherWidth; ',
- ' ',
- ' varying vec4 v_Color; ',
- ' varying vec4 v_ColorSecondary; ',
- ' varying vec2 v_relativeXy; ',
- ' varying float v_fillPattern; ',
- ' ',
- ' void pattern_stripe( ) { ',
- ' float stripeWidthTotal = u_stripeWidth + u_stripeSecondaryWidth; ',
- ' ',
- ' // calculate the value indicating where we are in the stripe pattern ',
- ' float stripeCoord = mod( v_relativeXy.x + u_slant * v_relativeXy.y , stripeWidthTotal ); ',
- ' ',
- ' // we are in the feather region beween the two stripes ',
- ' if ( stripeCoord < u_featherWidth ) { ',
- ' float diff = stripeCoord / u_featherWidth; ',
- ' gl_FragColor = vec4 ( v_Color.xyz * diff + (1.0-diff) * v_ColorSecondary.xyz, 1.0 ); ',
- ' } ',
- ' // we are in the color 1 stripe ',
- ' else if ( stripeCoord < u_stripeWidth ) { ',
- ' gl_FragColor = v_Color; ',
- ' } ',
- ' // we are the feather region between the two stripes ',
- ' else if ( stripeCoord < u_stripeWidth + u_featherWidth ) { ',
- ' float diff = ( stripeCoord - u_stripeWidth ) / u_featherWidth; ',
- ' gl_FragColor = vec4 ( v_Color.xyz * (1.0-diff) + diff * v_ColorSecondary.xyz, 1.0 ); ',
- ' } ',
- ' // we are in the color 2 stripe ',
- ' else { ',
- ' gl_FragColor = v_ColorSecondary; ',
- ' } ',
- ' } ',
- ' ',
- ' void pattern_gradient( ) { ',
- ' float stripeWidthTotal = u_stripeWidth + u_stripeSecondaryWidth; ',
- ' ',
- ' // calculate the value indicating where we are in the stripe pattern ',
- ' float stripeCoord = mod( v_relativeXy.x + u_slant * v_relativeXy.y , stripeWidthTotal ); ',
- ' ',
- ' float weightedCoord; ',
- ' if ( stripeCoord < u_stripeWidth ) { ',
- ' float slope = PI / u_stripeWidth; ',
- ' weightedCoord = slope * stripeCoord; ',
- ' } ',
- ' else { ',
- ' float slope = PI / u_stripeSecondaryWidth; ',
- ' weightedCoord = PI + slope * ( stripeCoord - u_stripeWidth ); ',
- ' } ',
- ' ',
- ' // sin wave domain: [0, stripeWidthTotal ] range: [0, 1] ',
- ' float frac = sin( weightedCoord ) * 2.0 - 1.0; ',
- ' ',
- ' // mix primary and secondary colors based on gradient fraction ',
- ' gl_FragColor = mix( v_Color, v_ColorSecondary, frac ); ',
- ' } ',
- ' ',
- ' void pattern_solid( ) { ',
- ' gl_FragColor = v_Color; ',
- ' } ',
- ' ',
- ' void main( ) { ',
- ' if ( v_fillPattern == 1.0 ) { ',
- ' pattern_stripe( ); ',
- ' } ',
- ' else if ( v_fillPattern == 2.0 ) { ',
- ' pattern_gradient( ); ',
- ' } ',
- ' else { ',
- ' pattern_solid( ); ',
- ' } ',
- ' } ',
- ' ',
- ' ',
- ' '
- );
-
- var program = new Program( xyFrac_vColor_VERTSHADER, fillPattern_FRAGSHADER );
- var a_XyFrac = new Attribute( program, 'a_XyFrac' );
- var a_Color = new Attribute( program, 'a_Color' );
- var a_ColorSecondary = new Attribute( program, 'a_ColorSecondary' );
- var a_relativeXy = new Attribute( program, 'a_relativeXy');
- var a_fillPattern = new Attribute( program, 'a_fillPattern');
-
- var u_stripeWidth = new Uniform1f( program, 'u_stripeWidth');
- var u_stripeSecondaryWidth = new Uniform1f( program, 'u_stripeSecondaryWidth');
- var u_slant = new Uniform1f( program, 'u_slant');
- var u_featherWidth = new Uniform1f( program, 'u_featherWidth');
-
- var xys = new Float32Array( 0 );
- var xysBuffer = newDynamicBuffer( );
-
- var rgbas = new Float32Array( 0 );
- var rgbasBuffer = newDynamicBuffer( );
-
- var rgbasSecondary = new Float32Array( 0 );
- var rgbasSecondaryBuffer = newDynamicBuffer( );
-
- var relativeXys = new Float32Array( 0 );
- var relativeXysBuffer = newDynamicBuffer( );
-
- var fillPattern = new Float32Array( 0 );
- var fillPatternBuffer = newDynamicBuffer( );
-
- return {
- paint( indexXys : number, indexRgbas : number, gl : WebGLRenderingContext, viewport : BoundsUnmodifiable, indexRelativeXys : number, indexFillPattern: number ) {
- if ( indexXys == 0 || indexRgbas == 0 ) return;
-
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
-
- program.use( gl );
-
- u_slant.setData( gl, stripeSlant );
- u_stripeWidth.setData( gl, stripeWidth );
- u_stripeSecondaryWidth.setData( gl, stripeSecondaryWidth );
- u_featherWidth.setData( gl, featherWidth );
-
- xysBuffer.setData( xys.subarray( 0, indexXys ) );
- a_XyFrac.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
-
- rgbasBuffer.setData( rgbas.subarray( 0, indexRgbas ) );
- a_Color.setDataAndEnable( gl, rgbasBuffer, 4, GL.FLOAT );
-
- rgbasSecondaryBuffer.setData( rgbasSecondary.subarray( 0, indexRgbas ) );
- a_ColorSecondary.setDataAndEnable( gl, rgbasSecondaryBuffer, 4, GL.FLOAT );
-
- relativeXysBuffer.setData( relativeXys.subarray(0, indexRelativeXys ) );
- a_relativeXy.setDataAndEnable( gl, relativeXysBuffer, 2, GL.FLOAT );
-
- fillPatternBuffer.setData( fillPattern.subarray(0, indexFillPattern) );
- a_fillPattern.setDataAndEnable( gl, fillPatternBuffer, 1, GL.FLOAT );
-
- gl.drawArrays( GL.TRIANGLES, 0, Math.floor( indexXys / 2 ) );
-
- a_Color.disable( gl );
- a_XyFrac.disable( gl );
- a_ColorSecondary.disable( gl );
- a_fillPattern.disable( gl );
- a_relativeXy.disable( gl );
-
- program.endUse( gl );
- },
- ensureCapacity: function( eventCount : number ) {
- var numVertices = ( 6*( 1 /*quads*/ ) )*eventCount;
- xys = ensureCapacityFloat32( xys, 2*numVertices );
- rgbas = ensureCapacityFloat32( rgbas, 4*numVertices );
- rgbasSecondary = ensureCapacityFloat32( rgbasSecondary, 4*numVertices );
- relativeXys = Webglimpse.ensureCapacityFloat32( relativeXys, 2 * numVertices );
- fillPattern = Webglimpse.ensureCapacityFloat32( fillPattern, numVertices );
- },
- fillEvent: function( laneIndex : number, eventIndex : number, indexXys : number, indexRgbas : number, viewport : BoundsUnmodifiable, indexRelativeXys: number, indexFillPattern: number ) : { indexXys : number; indexRgbas : number; indexRelativeXys: number; indexFillPattern: number; } {
- var lane : TimelineLane = lanes.lane( laneIndex );
- var event : TimelineEventModel = lane.event( eventIndex );
-
- var wBorder = borderThickness / viewport.w;
- var hBorder = borderThickness / viewport.h;
-
- var _topMargin = hasval( event.topMargin ) ? event.topMargin : topMargin;
- var _bottomMargin = hasval( event.bottomMargin ) ? event.bottomMargin : bottomMargin;
-
- var jTop = rowTopPadding + ( laneIndex )*laneHeight + _topMargin;
- var yTop = ( viewport.h - jTop ) / viewport.h;
- var jBottom = rowTopPadding + ( laneIndex + 1 )*laneHeight - _bottomMargin;
- var yBottom = ( viewport.h - jBottom ) / viewport.h;
-
- var xLeft = timeAxis.tFrac( event.start_PMILLIS );
- var xRight = timeAxis.tFrac( event.end_PMILLIS );
-
- var xWidthPixels = viewport.w * ( xRight - xLeft );
- var yHeightPixels = jTop - jBottom;
-
- if ( !( xRight < 0 || xLeft > 1 ) && xWidthPixels > minimumVisibleWidth ) {
-
- // Fill
- var fillColor = ( event.bgColor || defaultColor );
- var fillColorSecondary = ( event.bgSecondaryColor || defaultColorSecondary );
- if ( event === selection.hoveredEvent.value ) {
- fillColor = darker( fillColor, 0.8 );
- fillColorSecondary = darker( fillColorSecondary, 0.8 );
+ const selectedEvents: OrderedSet = ui.selection.selectedEvents;
+
+ // XXX Instead of estimating the number of events we will need to draw ahead of time
+ // XXX (difficult because selected events may be present in multiple lanes, so
+ // XXX selectedEvents.length might not be sufficient) just make enough space for all events.
+ // XXX Potentially quite inefficient with lots of events (and few selected events).
+ helper.ensureCapacity(lanes.numEvents);
+
+ let indexXys = 0;
+ let indexRgbas = 0;
+
+ for (let l = 0; l < lanes.length; l++) {
+ const lane = lanes.lane(l);
+ for (let e = 0; e < lane.length; e++) {
+ const event = lane.event(e);
+
+ // check whether the event is selected and has limits defined
+ if (selectedEvents.hasId(event.eventGuid) && (hasval(event.startLimit_PMILLIS) || hasval(event.endLimit_PMILLIS))) {
+ const indexes = helper.fillEvent(l, e, indexXys, indexRgbas, viewport);
+ indexXys = indexes.indexXys;
+ indexRgbas = indexes.indexRgbas;
}
- indexXys = putQuadXys( xys, indexXys, xLeft+wBorder, xRight-wBorder, yTop-hBorder, yBottom+hBorder );
-
- var startIndex = indexRgbas;
- putQuadRgbas( rgbas, startIndex, fillColor );
- indexRgbas = putQuadRgbas( rgbasSecondary, startIndex, fillColorSecondary );
-
- // create a quad with relative coordinates
- indexRelativeXys = putQuadXys(relativeXys, indexRelativeXys, 0.0, xWidthPixels, 0.0, yHeightPixels );
-
- // Set the fillPatternValue per vertex of the quad
- var fillPatternValue : number = event.fillPattern;
-
- fillPattern[indexFillPattern++] = fillPatternValue;
- fillPattern[indexFillPattern++] = fillPatternValue;
- fillPattern[indexFillPattern++] = fillPatternValue;
- fillPattern[indexFillPattern++] = fillPatternValue;
- fillPattern[indexFillPattern++] = fillPatternValue;
- fillPattern[indexFillPattern++] = fillPatternValue;
}
-
- return { indexXys : indexXys, indexRgbas : indexRgbas, indexRelativeXys: indexRelativeXys, indexFillPattern: indexFillPattern };
}
+
+ helper.paint(indexXys, indexRgbas, gl, viewport);
};
- }
+ };
+}
- function eventDashedBorderPainterHelper( barOpts : TimelineEventBarsPainterOptions, drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) {
- var rowTopPadding = options.rowTopPadding;
- var rowBottomPadding = options.rowBottomPadding;
- var laneHeight = options.laneHeight;
-
- var topMargin = ( hasval( barOpts ) && hasval( barOpts.topMargin ) ? barOpts.topMargin : 1.2 );
- var bottomMargin = ( hasval( barOpts ) && hasval( barOpts.bottomMargin ) ? barOpts.bottomMargin : 1.2 );
- var borderThickness = ( hasval( barOpts ) && hasval( barOpts.borderThickness ) ? barOpts.borderThickness : 2 );
- var cornerType = ( hasval( barOpts ) && hasval( barOpts.cornerType ) ? barOpts.cornerType : JointType.BEVEL );
- var defaultColor = ( hasval( barOpts ) && hasval( barOpts.defaultColor ) ? barOpts.defaultColor : options.timelineFgColor.withAlphaTimes( 0.4 ) );
- var defaultBorderColor = ( hasval( barOpts ) && hasval( barOpts.defaultBorderColor ) ? barOpts.defaultBorderColor : null );
- var selectedBorderColor = ( hasval( barOpts ) && hasval( barOpts.selectedBorderColor ) ? barOpts.selectedBorderColor : options.timelineFgColor );
- var minimumVisibleWidth = ( hasval( barOpts ) && hasval( barOpts.minimumVisibleWidth ) ? barOpts.minimumVisibleWidth : 0 );
- var dashLength = ( hasval( barOpts ) && hasval( barOpts.dashLength ) ? barOpts.dashLength : 5 );
- var defaultSecondaryColor = new Color( 0, 0, 0, 0 );
-
- var selection = ui.selection;
-
- var dashedBorder_VERTSHADER = concatLines(
- ' ',
- ' attribute vec2 a_XyFrac; ',
- ' attribute vec4 a_Color; ',
- ' attribute vec4 a_SecondaryColor; ',
- ' attribute float a_LengthSoFar; ',
- ' ',
- ' varying vec4 v_Color; ',
- ' varying vec4 v_SecondaryColor; ',
- ' varying float f_LengthSoFar; ',
- ' ',
- ' void main( ) { ',
- ' gl_Position = vec4( ( -1.0 + 2.0*a_XyFrac ), 0.0, 1.0 ); ',
- ' v_Color = a_Color; ',
- ' v_SecondaryColor = a_SecondaryColor; ',
- ' f_LengthSoFar = a_LengthSoFar; ',
- ' } ',
- ' '
- );
-
- var varyingBorder_FRAGSHADER = concatLines(
- ' ',
- ' precision lowp float; ',
- ' varying vec4 v_Color; ',
- ' varying vec4 v_SecondaryColor; ',
- ' varying float f_LengthSoFar; ',
- ' //dashes are u_DashLength_PX pixels long ',
- ' uniform float u_DashLength_PX; ',
- ' ',
- ' void main( ) { ',
- ' gl_FragColor = v_Color; ',
- ' ',
- ' if (f_LengthSoFar > 0.0) { ',
- ' float mod = mod(f_LengthSoFar, u_DashLength_PX * 2.0); ',
- ' float alpha = 1.0; ',
- ' if ( mod < u_DashLength_PX ) { ',
- ' gl_FragColor = v_SecondaryColor; ',
- ' } ',
- ' else { ',
- ' gl_FragColor = v_Color; ',
- ' } ',
- ' } ',
- ' else { ',
- ' gl_FragColor = v_Color; ',
- ' } ',
- ' } ',
- ' '
- );
-
- var program = new Program( dashedBorder_VERTSHADER, varyingBorder_FRAGSHADER );
- var a_XyFrac = new Attribute( program, 'a_XyFrac' );
- var a_Color = new Attribute( program, 'a_Color' );
- var a_SecondaryColor = new Attribute( program, 'a_SecondaryColor' );
- var a_LengthSoFar = new Attribute( program, 'a_LengthSoFar' );
- var u_DashLength_PX = new Uniform1f( program, 'u_DashLength_PX' );
-
- var xys = new Float32Array( 0 );
- var xysBuffer = newDynamicBuffer( );
-
- var rgbas = new Float32Array( 0 );
- var rgbasBuffer = newDynamicBuffer( );
-
- var rgbasSecondary = new Float32Array( 0 );
- var rgbasSecondaryBuffer = newDynamicBuffer( );
-
- var lengths = new Float32Array( 0 );
- var lengthsBuffer = newDynamicBuffer( );
-
- return {
- paint( indexXys : number, indexRgbas : number, gl : WebGLRenderingContext, viewport : BoundsUnmodifiable, indexLengthSoFar : number ) {
- if ( indexXys == 0 || indexRgbas == 0 ) return;
-
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
-
- program.use( gl );
- u_DashLength_PX.setData( gl, dashLength );
- xysBuffer.setData( xys.subarray( 0, indexXys ) );
- a_XyFrac.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
- rgbasBuffer.setData( rgbas.subarray( 0, indexRgbas ) );
- a_Color.setDataAndEnable( gl, rgbasBuffer, 4, GL.FLOAT );
- rgbasSecondaryBuffer.setData( rgbasSecondary.subarray( 0, indexRgbas ) );
- a_SecondaryColor.setDataAndEnable( gl, rgbasSecondaryBuffer, 4, GL.FLOAT );
- lengthsBuffer.setData( lengths.subarray( 0, indexLengthSoFar ) );
- a_LengthSoFar.setDataAndEnable( gl, lengthsBuffer, 1, GL.FLOAT );
-
- gl.drawArrays( GL.TRIANGLES, 0, Math.floor( indexXys / 2 ) );
-
- a_Color.disable( gl );
- a_SecondaryColor.disable( gl );
- a_XyFrac.disable( gl );
- a_LengthSoFar.disable( gl );
- program.endUse( gl );
- },
- ensureCapacity: function( eventCount : number ) {
- var numVertices;
- switch ( cornerType ) {
- case JointType.BEVEL:
- numVertices = ( 6*( 4 /*quads*/ ) + 3*( 4 /*triangles*/ ) )*eventCount;
- break;
-
- default:
- numVertices = ( 6*( 4 /*quads*/ ) )*eventCount;
- break;
- }
- xys = ensureCapacityFloat32( xys, 2*numVertices );
- rgbas = ensureCapacityFloat32( rgbas, 4*numVertices );
- rgbasSecondary = ensureCapacityFloat32( rgbasSecondary, 4*numVertices );
- lengths = ensureCapacityFloat32 ( lengths, numVertices );
- },
- fillEvent: function( laneIndex : number, eventIndex : number, indexXys : number, indexRgbas : number, viewport : BoundsUnmodifiable, indexLengthSoFar : number ) : { indexXys : number; indexRgbas : number; indexLengthSoFar : number } {
- var lane : TimelineLane = lanes.lane( laneIndex );
- var event : TimelineEventModel = lane.event( eventIndex );
-
- var wBorder = borderThickness / viewport.w;
- var hBorder = borderThickness / viewport.h;
-
- var _topMargin = hasval( event.topMargin ) ? event.topMargin : topMargin;
- var _bottomMargin = hasval( event.bottomMargin ) ? event.bottomMargin : bottomMargin;
-
- var jTop = rowTopPadding + ( laneIndex )*laneHeight + _topMargin;
- var yTop = ( viewport.h - jTop ) / viewport.h;
- var jBottom = rowTopPadding + ( laneIndex + 1 )*laneHeight - _bottomMargin;
- var yBottom = ( viewport.h - jBottom ) / viewport.h;
-
- var xLeft = timeAxis.tFrac( event.start_PMILLIS );
- var xRight = timeAxis.tFrac( event.end_PMILLIS );
-
- var widthPixels = viewport.w * ( xRight - xLeft );
- var heightPixels = jBottom - jTop; // confirmed jBottom > jTop
-
- var setLengthsVertical = function( bottomEdge, topEdge ) {
- lengths[ indexLengthSoFar++ ] = topEdge;
- lengths[ indexLengthSoFar++ ] = topEdge;
- lengths[ indexLengthSoFar++ ] = bottomEdge;
- lengths[ indexLengthSoFar++ ] = bottomEdge;
- lengths[ indexLengthSoFar++ ] = topEdge;
- lengths[ indexLengthSoFar++ ] = bottomEdge;
-
- // for convenience, return the length of the edge
- return Math.abs(bottomEdge - topEdge );
- };
-
- var setLengthsHorizontal = function( leftEdge, rightEdge ) {
- lengths[ indexLengthSoFar++ ] = leftEdge;
- lengths[ indexLengthSoFar++ ] = rightEdge;
- lengths[ indexLengthSoFar++ ] = leftEdge;
- lengths[ indexLengthSoFar++ ] = leftEdge;
- lengths[ indexLengthSoFar++ ] = rightEdge;
- lengths[ indexLengthSoFar++ ] = rightEdge;
-
- // for convenience, return the length of the edge
- return Math.abs(leftEdge - rightEdge);
- };
+export enum JointType {
+ BEVEL, MITER
+}
+
+export interface TimelineEventBarsPainterOptions {
+ topMargin?: number;
+ bottomMargin?: number;
+ borderThickness?: number;
+ cornerType?: JointType;
+ defaultColor?: Color;
+ defaultBorderColor?: Color;
+ selectedBorderColor?: Color;
+
+ // the size of the dashes when painting stippled event borders
+ dashLength?: number;
+ // the width of stripes in the painter fill
+ stripeWidth?: number;
+ // the width of secondary color stripes in the painter fill
+ stripeSecondaryWidth?: number;
+ // the slant of the stipes: 0 = horizontal, 1 = 45 degrees, -1 = 45 degrees backward
+ stripeSlant?: number;
+ // width in pixels of the antialiasing of the slant
+ featherWidth?: number;
+
+ // minimum pixel width of the event bar
+ // when the timeline is zoomed out so that the event bar
+ // is smaller than this visible width, the event bar is hidden
+ minimumVisibleWidth?: number;
+}
+
+export enum FillPattern {
+ solid = 0,
+ stripe = 1,
+ gradient = 2
+}
+
+function eventStripedBarPainterHelper(barOpts: TimelineEventBarsPainterOptions, drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions) {
+ const rowTopPadding = options.rowTopPadding;
+ const rowBottomPadding = options.rowBottomPadding;
+ const laneHeight = options.laneHeight;
+
+ const topMargin = (hasval(barOpts) && hasval(barOpts.topMargin) ? barOpts.topMargin : 1.2);
+ const bottomMargin = (hasval(barOpts) && hasval(barOpts.bottomMargin) ? barOpts.bottomMargin : 1.2);
+ const borderThickness = (hasval(barOpts) && hasval(barOpts.borderThickness) ? barOpts.borderThickness : 2);
+ const cornerType = (hasval(barOpts) && hasval(barOpts.cornerType) ? barOpts.cornerType : JointType.BEVEL);
+ const defaultColor = (hasval(barOpts) && hasval(barOpts.defaultColor) ? barOpts.defaultColor : options.timelineFgColor.withAlphaTimes(0.4));
+ const defaultColorSecondary = new Color(1, 1, 1, 1);
+ const minimumVisibleWidth = (hasval(barOpts) && hasval(barOpts.minimumVisibleWidth) ? barOpts.minimumVisibleWidth : 0);
+
+ const stripeWidth = (hasval(barOpts) && hasval(barOpts.stripeWidth) ? barOpts.stripeWidth : 5);
+ const stripeSecondaryWidth = (hasval(barOpts) && hasval(barOpts.stripeSecondaryWidth) ? barOpts.stripeSecondaryWidth : 5);
+ const stripeSlant = (hasval(barOpts) && hasval(barOpts.stripeSlant) ? barOpts.stripeSlant : 1);
+ const featherWidth = (hasval(barOpts) && hasval(barOpts.featherWidth) ? barOpts.featherWidth : 2);
+
+ const selection = ui.selection;
+
+ const xyFrac_vColor_VERTSHADER = concatLines(
+ ' ',
+ ' attribute vec2 a_XyFrac; ',
+ ' attribute vec4 a_Color; ',
+ ' attribute vec4 a_ColorSecondary; ',
+ ' attribute vec2 a_relativeXy; ',
+ ' attribute float a_fillPattern; ',
+ ' ',
+ ' varying vec4 v_Color; ',
+ ' varying vec4 v_ColorSecondary; ',
+ ' varying vec2 v_relativeXy; ',
+ ' varying float v_fillPattern; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_Position = vec4( ( -1.0 + 2.0*a_XyFrac ), 0.0, 1.0 ); ',
+ ' v_Color = a_Color; ',
+ ' v_ColorSecondary = a_ColorSecondary; ',
+ ' v_relativeXy = a_relativeXy; ',
+ ' v_fillPattern = a_fillPattern; ',
+ ' } ',
+ ' '
+ );
+
+ const fillPattern_FRAGSHADER = concatLines(
+ ' #define PI 3.1415926535897932384626433832795 ',
+ ' ',
+ ' precision lowp float; ',
+ ' // the width in pixels of the first color stripe ',
+ ' uniform float u_stripeWidth; ',
+ ' // the width in pixels of the second color stripe ',
+ ' uniform float u_stripeSecondaryWidth; ',
+ ' // the slant of the stipes: 0 = horizontal, 1 = 45 degrees ',
+ ' uniform float u_slant; ',
+ ' // width in pixels of the antialiasing of the slant ',
+ ' uniform float u_featherWidth; ',
+ ' ',
+ ' varying vec4 v_Color; ',
+ ' varying vec4 v_ColorSecondary; ',
+ ' varying vec2 v_relativeXy; ',
+ ' varying float v_fillPattern; ',
+ ' ',
+ ' void pattern_stripe( ) { ',
+ ' float stripeWidthTotal = u_stripeWidth + u_stripeSecondaryWidth; ',
+ ' ',
+ ' // calculate the value indicating where we are in the stripe pattern ',
+ ' float stripeCoord = mod( v_relativeXy.x + u_slant * v_relativeXy.y , stripeWidthTotal ); ',
+ ' ',
+ ' // we are in the feather region beween the two stripes ',
+ ' if ( stripeCoord < u_featherWidth ) { ',
+ ' float diff = stripeCoord / u_featherWidth; ',
+ ' gl_FragColor = vec4 ( v_Color.xyz * diff + (1.0-diff) * v_ColorSecondary.xyz, 1.0 ); ',
+ ' } ',
+ ' // we are in the color 1 stripe ',
+ ' else if ( stripeCoord < u_stripeWidth ) { ',
+ ' gl_FragColor = v_Color; ',
+ ' } ',
+ ' // we are the feather region between the two stripes ',
+ ' else if ( stripeCoord < u_stripeWidth + u_featherWidth ) { ',
+ ' float diff = ( stripeCoord - u_stripeWidth ) / u_featherWidth; ',
+ ' gl_FragColor = vec4 ( v_Color.xyz * (1.0-diff) + diff * v_ColorSecondary.xyz, 1.0 ); ',
+ ' } ',
+ ' // we are in the color 2 stripe ',
+ ' else { ',
+ ' gl_FragColor = v_ColorSecondary; ',
+ ' } ',
+ ' } ',
+ ' ',
+ ' void pattern_gradient( ) { ',
+ ' float stripeWidthTotal = u_stripeWidth + u_stripeSecondaryWidth; ',
+ ' ',
+ ' // calculate the value indicating where we are in the stripe pattern ',
+ ' float stripeCoord = mod( v_relativeXy.x + u_slant * v_relativeXy.y , stripeWidthTotal ); ',
+ ' ',
+ ' float weightedCoord; ',
+ ' if ( stripeCoord < u_stripeWidth ) { ',
+ ' float slope = PI / u_stripeWidth; ',
+ ' weightedCoord = slope * stripeCoord; ',
+ ' } ',
+ ' else { ',
+ ' float slope = PI / u_stripeSecondaryWidth; ',
+ ' weightedCoord = PI + slope * ( stripeCoord - u_stripeWidth ); ',
+ ' } ',
+ ' ',
+ ' // sin wave domain: [0, stripeWidthTotal ] range: [0, 1] ',
+ ' float frac = sin( weightedCoord ) * 2.0 - 1.0; ',
+ ' ',
+ ' // mix primary and secondary colors based on gradient fraction ',
+ ' gl_FragColor = mix( v_Color, v_ColorSecondary, frac ); ',
+ ' } ',
+ ' ',
+ ' void pattern_solid( ) { ',
+ ' gl_FragColor = v_Color; ',
+ ' } ',
+ ' ',
+ ' void main( ) { ',
+ ' if ( v_fillPattern == 1.0 ) { ',
+ ' pattern_stripe( ); ',
+ ' } ',
+ ' else if ( v_fillPattern == 2.0 ) { ',
+ ' pattern_gradient( ); ',
+ ' } ',
+ ' else { ',
+ ' pattern_solid( ); ',
+ ' } ',
+ ' } ',
+ ' ',
+ ' ',
+ ' '
+ );
+
+ const program = new Program(xyFrac_vColor_VERTSHADER, fillPattern_FRAGSHADER);
+ const a_XyFrac = new Attribute(program, 'a_XyFrac');
+ const a_Color = new Attribute(program, 'a_Color');
+ const a_ColorSecondary = new Attribute(program, 'a_ColorSecondary');
+ const a_relativeXy = new Attribute(program, 'a_relativeXy');
+ const a_fillPattern = new Attribute(program, 'a_fillPattern');
+
+ const u_stripeWidth = new Uniform1f(program, 'u_stripeWidth');
+ const u_stripeSecondaryWidth = new Uniform1f(program, 'u_stripeSecondaryWidth');
+ const u_slant = new Uniform1f(program, 'u_slant');
+ const u_featherWidth = new Uniform1f(program, 'u_featherWidth');
+
+ let xys = new Float32Array(0);
+ const xysBuffer = newDynamicBuffer();
+
+ let rgbas = new Float32Array(0);
+ const rgbasBuffer = newDynamicBuffer();
+
+ let rgbasSecondary = new Float32Array(0);
+ const rgbasSecondaryBuffer = newDynamicBuffer();
+
+ let relativeXys = new Float32Array(0);
+ const relativeXysBuffer = newDynamicBuffer();
+
+ let fillPattern = new Float32Array(0);
+ const fillPatternBuffer = newDynamicBuffer();
+
+ return {
+ paint(indexXys: number, indexRgbas: number, gl: WebGLRenderingContext, viewport: BoundsUnmodifiable, indexRelativeXys: number, indexFillPattern: number) {
+ if (indexXys === 0 || indexRgbas === 0) {
+ return;
+ }
+
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ program.use(gl);
+
+ u_slant.setData(gl, stripeSlant);
+ u_stripeWidth.setData(gl, stripeWidth);
+ u_stripeSecondaryWidth.setData(gl, stripeSecondaryWidth);
+ u_featherWidth.setData(gl, featherWidth);
+
+ xysBuffer.setData(xys.subarray(0, indexXys));
+ a_XyFrac.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+
+ rgbasBuffer.setData(rgbas.subarray(0, indexRgbas));
+ a_Color.setDataAndEnable(gl, rgbasBuffer, 4, GL.FLOAT);
+
+ rgbasSecondaryBuffer.setData(rgbasSecondary.subarray(0, indexRgbas));
+ a_ColorSecondary.setDataAndEnable(gl, rgbasSecondaryBuffer, 4, GL.FLOAT);
- var setLengthsTriangle = function( length ) {
- lengths[ indexLengthSoFar++ ] = length;
- lengths[ indexLengthSoFar++ ] = length;
- lengths[ indexLengthSoFar++ ] = length;
+ relativeXysBuffer.setData(relativeXys.subarray(0, indexRelativeXys));
+ a_relativeXy.setDataAndEnable(gl, relativeXysBuffer, 2, GL.FLOAT);
+
+ fillPatternBuffer.setData(fillPattern.subarray(0, indexFillPattern));
+ a_fillPattern.setDataAndEnable(gl, fillPatternBuffer, 1, GL.FLOAT);
+
+ gl.drawArrays(GL.TRIANGLES, 0, Math.floor(indexXys / 2));
+
+ a_Color.disable(gl);
+ a_XyFrac.disable(gl);
+ a_ColorSecondary.disable(gl);
+ a_fillPattern.disable(gl);
+ a_relativeXy.disable(gl);
+
+ program.endUse(gl);
+ },
+ ensureCapacity: function (eventCount: number) {
+ const numVertices = (6 * (1 /*quads*/)) * eventCount;
+ xys = ensureCapacityFloat32(xys, 2 * numVertices);
+ rgbas = ensureCapacityFloat32(rgbas, 4 * numVertices);
+ rgbasSecondary = ensureCapacityFloat32(rgbasSecondary, 4 * numVertices);
+ relativeXys = ensureCapacityFloat32(relativeXys, 2 * numVertices);
+ fillPattern = ensureCapacityFloat32(fillPattern, numVertices);
+ },
+ fillEvent: function (laneIndex: number, eventIndex: number, indexXys: number, indexRgbas: number, viewport: BoundsUnmodifiable, indexRelativeXys: number, indexFillPattern: number): { indexXys: number; indexRgbas: number; indexRelativeXys: number; indexFillPattern: number; } {
+ const lane: TimelineLane = lanes.lane(laneIndex);
+ const event: TimelineEventModel = lane.event(eventIndex);
+
+ const wBorder = borderThickness / viewport.w;
+ const hBorder = borderThickness / viewport.h;
+
+ const _topMargin = hasval(event.topMargin) ? event.topMargin : topMargin;
+ const _bottomMargin = hasval(event.bottomMargin) ? event.bottomMargin : bottomMargin;
+
+ const jTop = rowTopPadding + (laneIndex) * laneHeight + _topMargin;
+ const yTop = (viewport.h - jTop) / viewport.h;
+ const jBottom = rowTopPadding + (laneIndex + 1) * laneHeight - _bottomMargin;
+ const yBottom = (viewport.h - jBottom) / viewport.h;
+
+ const xLeft = timeAxis.tFrac(event.start_PMILLIS);
+ const xRight = timeAxis.tFrac(event.end_PMILLIS);
+
+ const xWidthPixels = viewport.w * (xRight - xLeft);
+ const yHeightPixels = jTop - jBottom;
+
+ if (!(xRight < 0 || xLeft > 1) && xWidthPixels > minimumVisibleWidth) {
+
+ // Fill
+ let fillColor = (event.bgColor || defaultColor);
+ let fillColorSecondary = (event.bgSecondaryColor || defaultColorSecondary);
+ if (event === selection.hoveredEvent.value) {
+ fillColor = darker(fillColor, 0.8);
+ fillColorSecondary = darker(fillColorSecondary, 0.8);
}
-
- if ( !( xRight < 0 || xLeft > 1 ) && widthPixels > minimumVisibleWidth ) {
-
- // Border
- var borderColor = ( event.borderColor || event.bgColor || defaultBorderColor );
- var borderSecondaryColor = ( event.borderSecondaryColor || defaultSecondaryColor );
- if ( selection.selectedEvents.hasValue( event ) ) {
- borderColor = selectedBorderColor;
- }
- if ( borderColor ) {
- switch ( cornerType ) {
- case JointType.BEVEL:
- // Quads
+ indexXys = putQuadXys(xys, indexXys, xLeft + wBorder, xRight - wBorder, yTop - hBorder, yBottom + hBorder);
+
+ const startIndex = indexRgbas;
+ putQuadRgbas(rgbas, startIndex, fillColor);
+ indexRgbas = putQuadRgbas(rgbasSecondary, startIndex, fillColorSecondary);
+
+ // create a quad with relative coordinates
+ indexRelativeXys = putQuadXys(relativeXys, indexRelativeXys, 0.0, xWidthPixels, 0.0, yHeightPixels);
+
+ // Set the fillPatternValue per vertex of the quad
+ const fillPatternValue: number = event.fillPattern;
+
+ fillPattern[indexFillPattern++] = fillPatternValue;
+ fillPattern[indexFillPattern++] = fillPatternValue;
+ fillPattern[indexFillPattern++] = fillPatternValue;
+ fillPattern[indexFillPattern++] = fillPatternValue;
+ fillPattern[indexFillPattern++] = fillPatternValue;
+ fillPattern[indexFillPattern++] = fillPatternValue;
+ }
+
+ return { indexXys: indexXys, indexRgbas: indexRgbas, indexRelativeXys: indexRelativeXys, indexFillPattern: indexFillPattern };
+ }
+ };
+}
+
+function eventDashedBorderPainterHelper(barOpts: TimelineEventBarsPainterOptions, drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions) {
+ const rowTopPadding = options.rowTopPadding;
+ const rowBottomPadding = options.rowBottomPadding;
+ const laneHeight = options.laneHeight;
+
+ const topMargin = (hasval(barOpts) && hasval(barOpts.topMargin) ? barOpts.topMargin : 1.2);
+ const bottomMargin = (hasval(barOpts) && hasval(barOpts.bottomMargin) ? barOpts.bottomMargin : 1.2);
+ const borderThickness = (hasval(barOpts) && hasval(barOpts.borderThickness) ? barOpts.borderThickness : 2);
+ const cornerType = (hasval(barOpts) && hasval(barOpts.cornerType) ? barOpts.cornerType : JointType.BEVEL);
+ const defaultColor = (hasval(barOpts) && hasval(barOpts.defaultColor) ? barOpts.defaultColor : options.timelineFgColor.withAlphaTimes(0.4));
+ const defaultBorderColor = (hasval(barOpts) && hasval(barOpts.defaultBorderColor) ? barOpts.defaultBorderColor : null);
+ const selectedBorderColor = (hasval(barOpts) && hasval(barOpts.selectedBorderColor) ? barOpts.selectedBorderColor : null);
+ const minimumVisibleWidth = (hasval(barOpts) && hasval(barOpts.minimumVisibleWidth) ? barOpts.minimumVisibleWidth : 0);
+ const dashLength = (hasval(barOpts) && hasval(barOpts.dashLength) ? barOpts.dashLength : 5);
+ const defaultSecondaryColor = new Color(0, 0, 0, 0);
+
+ const selection = ui.selection;
+
+ const dashedBorder_VERTSHADER = concatLines(
+ ' ',
+ ' attribute vec2 a_XyFrac; ',
+ ' attribute vec4 a_Color; ',
+ ' attribute vec4 a_SecondaryColor; ',
+ ' attribute float a_LengthSoFar; ',
+ ' ',
+ ' varying vec4 v_Color; ',
+ ' varying vec4 v_SecondaryColor; ',
+ ' varying float f_LengthSoFar; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_Position = vec4( ( -1.0 + 2.0*a_XyFrac ), 0.0, 1.0 ); ',
+ ' v_Color = a_Color; ',
+ ' v_SecondaryColor = a_SecondaryColor; ',
+ ' f_LengthSoFar = a_LengthSoFar; ',
+ ' } ',
+ ' '
+ );
+
+ const varyingBorder_FRAGSHADER = concatLines(
+ ' ',
+ ' precision lowp float; ',
+ ' varying vec4 v_Color; ',
+ ' varying vec4 v_SecondaryColor; ',
+ ' varying float f_LengthSoFar; ',
+ ' //dashes are u_DashLength_PX pixels long ',
+ ' uniform float u_DashLength_PX; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_FragColor = v_Color; ',
+ ' ',
+ ' if (f_LengthSoFar > 0.0) { ',
+ ' float mod = mod(f_LengthSoFar, u_DashLength_PX * 2.0); ',
+ ' float alpha = 1.0; ',
+ ' if ( mod < u_DashLength_PX ) { ',
+ ' gl_FragColor = v_SecondaryColor; ',
+ ' } ',
+ ' else { ',
+ ' gl_FragColor = v_Color; ',
+ ' } ',
+ ' } ',
+ ' else { ',
+ ' gl_FragColor = v_Color; ',
+ ' } ',
+ ' } ',
+ ' '
+ );
+
+ const program = new Program(dashedBorder_VERTSHADER, varyingBorder_FRAGSHADER);
+ const a_XyFrac = new Attribute(program, 'a_XyFrac');
+ const a_Color = new Attribute(program, 'a_Color');
+ const a_SecondaryColor = new Attribute(program, 'a_SecondaryColor');
+ const a_LengthSoFar = new Attribute(program, 'a_LengthSoFar');
+ const u_DashLength_PX = new Uniform1f(program, 'u_DashLength_PX');
+
+ let xys = new Float32Array(0);
+ const xysBuffer = newDynamicBuffer();
+
+ let rgbas = new Float32Array(0);
+ const rgbasBuffer = newDynamicBuffer();
+
+ let rgbasSecondary = new Float32Array(0);
+ const rgbasSecondaryBuffer = newDynamicBuffer();
+
+ let lengths = new Float32Array(0);
+ const lengthsBuffer = newDynamicBuffer();
+
+ return {
+ paint(indexXys: number, indexRgbas: number, gl: WebGLRenderingContext, viewport: BoundsUnmodifiable, indexLengthSoFar: number) {
+ if (indexXys === 0 || indexRgbas === 0) {
+ return;
+ }
+
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ program.use(gl);
+ u_DashLength_PX.setData(gl, dashLength);
+ xysBuffer.setData(xys.subarray(0, indexXys));
+ a_XyFrac.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+ rgbasBuffer.setData(rgbas.subarray(0, indexRgbas));
+ a_Color.setDataAndEnable(gl, rgbasBuffer, 4, GL.FLOAT);
+ rgbasSecondaryBuffer.setData(rgbasSecondary.subarray(0, indexRgbas));
+ a_SecondaryColor.setDataAndEnable(gl, rgbasSecondaryBuffer, 4, GL.FLOAT);
+ lengthsBuffer.setData(lengths.subarray(0, indexLengthSoFar));
+ a_LengthSoFar.setDataAndEnable(gl, lengthsBuffer, 1, GL.FLOAT);
+
+ gl.drawArrays(GL.TRIANGLES, 0, Math.floor(indexXys / 2));
+
+ a_Color.disable(gl);
+ a_SecondaryColor.disable(gl);
+ a_XyFrac.disable(gl);
+ a_LengthSoFar.disable(gl);
+ program.endUse(gl);
+ },
+ ensureCapacity: function (eventCount: number) {
+ let numVertices;
+ switch (cornerType) {
+ case JointType.BEVEL:
+ numVertices = (6 * (4 /*quads*/) + 3 * (4 /*triangles*/)) * eventCount;
+ break;
+
+ default:
+ numVertices = (6 * (4 /*quads*/)) * eventCount;
+ break;
+ }
+ xys = ensureCapacityFloat32(xys, 2 * numVertices);
+ rgbas = ensureCapacityFloat32(rgbas, 4 * numVertices);
+ rgbasSecondary = ensureCapacityFloat32(rgbasSecondary, 4 * numVertices);
+ lengths = ensureCapacityFloat32(lengths, numVertices);
+ },
+ fillEvent: function (laneIndex: number, eventIndex: number, indexXys: number, indexRgbas: number, viewport: BoundsUnmodifiable, indexLengthSoFar: number): { indexXys: number; indexRgbas: number; indexLengthSoFar: number } {
+ const lane: TimelineLane = lanes.lane(laneIndex);
+ const event: TimelineEventModel = lane.event(eventIndex);
+
+ const wBorder = borderThickness / viewport.w;
+ const hBorder = borderThickness / viewport.h;
+
+ const _topMargin = hasval(event.topMargin) ? event.topMargin : topMargin;
+ const _bottomMargin = hasval(event.bottomMargin) ? event.bottomMargin : bottomMargin;
+
+ const jTop = rowTopPadding + (laneIndex) * laneHeight + _topMargin;
+ const yTop = (viewport.h - jTop) / viewport.h;
+ const jBottom = rowTopPadding + (laneIndex + 1) * laneHeight - _bottomMargin;
+ const yBottom = (viewport.h - jBottom) / viewport.h;
+
+ const xLeft = timeAxis.tFrac(event.start_PMILLIS);
+ const xRight = timeAxis.tFrac(event.end_PMILLIS);
+
+ const widthPixels = viewport.w * (xRight - xLeft);
+ const heightPixels = jBottom - jTop; // confirmed jBottom > jTop
+
+ const setLengthsVertical = function (bottomEdge: number, topEdge: number) {
+ lengths[indexLengthSoFar++] = topEdge;
+ lengths[indexLengthSoFar++] = topEdge;
+ lengths[indexLengthSoFar++] = bottomEdge;
+ lengths[indexLengthSoFar++] = bottomEdge;
+ lengths[indexLengthSoFar++] = topEdge;
+ lengths[indexLengthSoFar++] = bottomEdge;
+
+ // for convenience, return the length of the edge
+ return Math.abs(bottomEdge - topEdge);
+ };
+
+ const setLengthsHorizontal = function (leftEdge: number, rightEdge: number) {
+ lengths[indexLengthSoFar++] = leftEdge;
+ lengths[indexLengthSoFar++] = rightEdge;
+ lengths[indexLengthSoFar++] = leftEdge;
+ lengths[indexLengthSoFar++] = leftEdge;
+ lengths[indexLengthSoFar++] = rightEdge;
+ lengths[indexLengthSoFar++] = rightEdge;
+
+ // for convenience, return the length of the edge
+ return Math.abs(leftEdge - rightEdge);
+ };
+
+ const setLengthsTriangle = function (length: number) {
+ lengths[indexLengthSoFar++] = length;
+ lengths[indexLengthSoFar++] = length;
+ lengths[indexLengthSoFar++] = length;
+ };
+
+ if (!(xRight < 0 || xLeft > 1) && widthPixels > minimumVisibleWidth) {
+
+ // Border
+ let borderColor = (event.borderColor || event.bgColor || defaultBorderColor);
+ const borderSecondaryColor = (event.borderSecondaryColor || defaultSecondaryColor);
+ if (selection.selectedEvents.hasValue(event)) {
+ borderColor = (selectedBorderColor || borderColor);
+ }
+ if (borderColor) {
+ let startIndex = 0;
+ switch (cornerType) {
+ case JointType.BEVEL:
+ // Quads
+
+ // top edge
+ indexXys = putQuadXys(xys, indexXys, xLeft + wBorder, xRight - wBorder, yTop, yTop - hBorder);
+ indexXys = putUpperRightTriangleXys(xys, indexXys, xLeft, xLeft + wBorder, yBottom + hBorder, yBottom);
+ // right edge
+ indexXys = putQuadXys(xys, indexXys, xRight - wBorder, xRight, yTop - hBorder, yBottom + hBorder);
+ indexXys = putLowerRightTriangleXys(xys, indexXys, xLeft, xLeft + wBorder, yTop, yTop - hBorder);
+ // bottom edge
+ indexXys = putQuadXys(xys, indexXys, xLeft + wBorder, xRight - wBorder, yBottom + hBorder, yBottom);
+ indexXys = putLowerLeftTriangleXys(xys, indexXys, xRight - wBorder, xRight, yTop, yTop - hBorder);
+ // left edge
+ indexXys = putQuadXys(xys, indexXys, xLeft, xLeft + wBorder, yTop - hBorder, yBottom + hBorder);
+ indexXys = putUpperLeftTriangleXys(xys, indexXys, xRight - wBorder, xRight, yBottom + hBorder, yBottom);
+
+ // Colors
+ startIndex = indexRgbas;
+ putRgbas(rgbas, startIndex, borderColor, 24);
+ indexRgbas = putRgbas(rgbasSecondary, startIndex, borderSecondaryColor, 24);
+
+ // Colors
+ startIndex = indexRgbas;
+ putRgbas(rgbas, startIndex, borderColor, 12);
+ indexRgbas = putRgbas(rgbasSecondary, startIndex, borderSecondaryColor, 12);
+
+ // Stipple
+ if (!event.isBorderDashed) {
+ setLengthsHorizontal(-1, -1);
+ setLengthsTriangle(-1);
+ setLengthsVertical(-1, -1);
+ setLengthsTriangle(-1);
+ setLengthsHorizontal(-1, -1);
+ setLengthsTriangle(-1);
+ setLengthsVertical(-1, -1);
+ setLengthsTriangle(-1);
+ }
+ else {
+ let cumulativeLength = 0;
// top edge
- indexXys = putQuadXys( xys, indexXys, xLeft+wBorder, xRight-wBorder, yTop, yTop-hBorder );
- indexXys = putUpperRightTriangleXys( xys, indexXys, xLeft, xLeft+wBorder, yBottom+hBorder, yBottom );
+ cumulativeLength += setLengthsHorizontal(cumulativeLength, cumulativeLength + widthPixels);
+ setLengthsTriangle(cumulativeLength);
// right edge
- indexXys = putQuadXys( xys, indexXys, xRight-wBorder, xRight, yTop-hBorder, yBottom+hBorder );
- indexXys = putLowerRightTriangleXys( xys, indexXys, xLeft, xLeft+wBorder, yTop, yTop-hBorder );
+ cumulativeLength += setLengthsVertical(cumulativeLength + heightPixels, cumulativeLength);
+ setLengthsTriangle(cumulativeLength);
// bottom edge
- indexXys = putQuadXys( xys, indexXys, xLeft+wBorder, xRight-wBorder, yBottom+hBorder, yBottom );
- indexXys = putLowerLeftTriangleXys( xys, indexXys, xRight-wBorder, xRight, yTop, yTop-hBorder );
+ cumulativeLength += setLengthsHorizontal(cumulativeLength, cumulativeLength + widthPixels);
+ setLengthsTriangle(cumulativeLength);
// left edge
- indexXys = putQuadXys( xys, indexXys, xLeft, xLeft+wBorder, yTop-hBorder, yBottom+hBorder );
- indexXys = putUpperLeftTriangleXys( xys, indexXys, xRight-wBorder, xRight, yBottom+hBorder, yBottom );
-
- // Colors
- var startIndex = indexRgbas;
- putRgbas( rgbas, startIndex, borderColor, 24 );
- indexRgbas = putRgbas( rgbasSecondary, startIndex, borderSecondaryColor, 24 );
-
- // Colors
- startIndex = indexRgbas;
- putRgbas( rgbas, startIndex, borderColor, 12 );
- indexRgbas = putRgbas( rgbasSecondary, startIndex, borderSecondaryColor, 12 );
-
- // Stipple
- if ( !event.isBorderDashed ) {
- setLengthsHorizontal(-1, -1);
- setLengthsTriangle( -1 );
- setLengthsVertical(-1, -1);
- setLengthsTriangle( -1 );
- setLengthsHorizontal(-1, -1);
- setLengthsTriangle( -1 );
- setLengthsVertical(-1, -1);
- setLengthsTriangle( -1 );
- }
- else {
- var cumulativeLength = 0;
- // top edge
- cumulativeLength += setLengthsHorizontal(cumulativeLength, cumulativeLength + widthPixels);
- setLengthsTriangle( cumulativeLength );
- // right edge
- cumulativeLength += setLengthsVertical(cumulativeLength + heightPixels, cumulativeLength);
- setLengthsTriangle( cumulativeLength );
- // bottom edge
- cumulativeLength += setLengthsHorizontal(cumulativeLength, cumulativeLength + widthPixels);
- setLengthsTriangle( cumulativeLength );
- // left edge
- cumulativeLength += setLengthsVertical(cumulativeLength + heightPixels , cumulativeLength);
- setLengthsTriangle( cumulativeLength );
- }
-
- break;
-
- default:
+ cumulativeLength += setLengthsVertical(cumulativeLength + heightPixels, cumulativeLength);
+ setLengthsTriangle(cumulativeLength);
+ }
+ break;
+
+ default:
+
+ // top edge
+ indexXys = putQuadXys(xys, indexXys, xLeft, xRight - wBorder, yTop, yTop - hBorder);
+ // right edge
+ indexXys = putQuadXys(xys, indexXys, xRight - wBorder, xRight, yTop, yBottom + hBorder);
+ // bottom edge
+ indexXys = putQuadXys(xys, indexXys, xLeft + wBorder, xRight, yBottom + hBorder, yBottom);
+ // left edge
+ indexXys = putQuadXys(xys, indexXys, xLeft, xLeft + wBorder, yTop - hBorder, yBottom);
+ // color
+ startIndex = indexRgbas;
+ putRgbas(rgbas, startIndex, borderColor, 24);
+ indexRgbas = putRgbas(rgbasSecondary, startIndex, borderSecondaryColor, 24);
+
+ // Stipple
+ if (!event.isBorderDashed) {
+ setLengthsHorizontal(-1, -1);
+ setLengthsVertical(-1, -1);
+ setLengthsHorizontal(-1, -1);
+ setLengthsVertical(-1, -1);
+ }
+ else {
+ let cumulativeLength = 0;
// top edge
- indexXys = putQuadXys( xys, indexXys, xLeft, xRight-wBorder, yTop, yTop-hBorder );
+ cumulativeLength += setLengthsHorizontal(cumulativeLength, cumulativeLength + widthPixels);
// right edge
- indexXys = Webglimpse.putQuadXys(xys, indexXys, xRight - wBorder, xRight, yTop, yBottom + hBorder);
+ cumulativeLength += setLengthsVertical(cumulativeLength + heightPixels, cumulativeLength);
// bottom edge
- indexXys = Webglimpse.putQuadXys(xys, indexXys, xLeft + wBorder, xRight, yBottom + hBorder, yBottom);
+ cumulativeLength += setLengthsHorizontal(cumulativeLength, cumulativeLength + widthPixels);
// left edge
- indexXys = Webglimpse.putQuadXys(xys, indexXys, xLeft, xLeft + wBorder, yTop - hBorder, yBottom);
- // color
- var startIndex = indexRgbas;
- putRgbas( rgbas, startIndex, borderColor, 24 );
- indexRgbas = putRgbas( rgbasSecondary, startIndex, borderSecondaryColor, 24 );
-
- // Stipple
- if ( !event.isBorderDashed ) {
- setLengthsHorizontal(-1, -1);
- setLengthsVertical(-1, -1);
- setLengthsHorizontal(-1, -1);
- setLengthsVertical(-1, -1);
- }
- else {
- var cumulativeLength = 0;
- // top edge
- cumulativeLength += setLengthsHorizontal(cumulativeLength, cumulativeLength + widthPixels);
- // right edge
- cumulativeLength += setLengthsVertical(cumulativeLength + heightPixels, cumulativeLength);
- // bottom edge
- cumulativeLength += setLengthsHorizontal(cumulativeLength, cumulativeLength + widthPixels);
- // left edge
- cumulativeLength += setLengthsVertical(cumulativeLength + heightPixels , cumulativeLength);
- }
-
- break;
- }
+ cumulativeLength += setLengthsVertical(cumulativeLength + heightPixels, cumulativeLength);
+ }
+
+ break;
}
}
-
- return { indexXys : indexXys, indexRgbas : indexRgbas, indexLengthSoFar : indexLengthSoFar };
}
- };
- }
- export function newEventStripedBarsPainterFactory( barOpts? : TimelineEventBarsPainterOptions ) : TimelineEventsPainterFactory {
-
- // Painter Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) : Painter {
-
- var helper = eventStripedBarPainterHelper( barOpts, drawable, timeAxis, lanes, ui, options );
-
- // Painter
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- helper.ensureCapacity( lanes.numEvents );
-
- var indexXys = 0;
- var indexRgbas = 0;
- var indexRelativeXys = 0;
- var indexFillPattern = 0;
-
- for ( var l = 0; l < lanes.length; l++ ) {
- var lane = lanes.lane( l );
- for ( var e = 0; e < lane.length; e++ ) {
- var event = lane.event( e );
- var indexes = helper.fillEvent( l, e, indexXys, indexRgbas, viewport, indexRelativeXys, indexFillPattern);
- indexXys = indexes.indexXys;
- indexRgbas = indexes.indexRgbas;
- indexRelativeXys = indexes.indexRelativeXys;
- indexFillPattern = indexes.indexFillPattern;
- }
+ return { indexXys: indexXys, indexRgbas: indexRgbas, indexLengthSoFar: indexLengthSoFar };
+ }
+ };
+}
+
+export function newEventStripedBarsPainterFactory(barOpts?: TimelineEventBarsPainterOptions): TimelineEventsPainterFactory {
+
+ // Painter Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions): Painter {
+
+ const helper = eventStripedBarPainterHelper(barOpts, drawable, timeAxis, lanes, ui, options);
+
+ // Painter
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ helper.ensureCapacity(lanes.numEvents);
+
+ let indexXys = 0;
+ let indexRgbas = 0;
+ let indexRelativeXys = 0;
+ let indexFillPattern = 0;
+
+ for (let l = 0; l < lanes.length; l++) {
+ const lane = lanes.lane(l);
+ for (let e = 0; e < lane.length; e++) {
+ const event = lane.event(e);
+ const indexes = helper.fillEvent(l, e, indexXys, indexRgbas, viewport, indexRelativeXys, indexFillPattern);
+ indexXys = indexes.indexXys;
+ indexRgbas = indexes.indexRgbas;
+ indexRelativeXys = indexes.indexRelativeXys;
+ indexFillPattern = indexes.indexFillPattern;
}
+ }
- helper.paint( indexXys, indexRgbas, gl, viewport, indexRelativeXys, indexFillPattern);
- };
+ helper.paint(indexXys, indexRgbas, gl, viewport, indexRelativeXys, indexFillPattern);
};
- }
-
- export function newEventDashedBordersPainterFactory( barOpts? : TimelineEventBarsPainterOptions ) : TimelineEventsPainterFactory {
-
- // Painter Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) : Painter {
-
- var helper = eventDashedBorderPainterHelper( barOpts, drawable, timeAxis, lanes, ui, options );
-
- // Painter
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- helper.ensureCapacity( lanes.numEvents );
-
- var indexXys = 0;
- var indexRgbas = 0;
- var indexLengthSoFar = 0;
-
- for ( var l = 0; l < lanes.length; l++ ) {
- var lane = lanes.lane( l );
- for ( var e = 0; e < lane.length; e++ ) {
- var event = lane.event( e );
- var indexes = helper.fillEvent( l, e, indexXys, indexRgbas, viewport, indexLengthSoFar );
- indexXys = indexes.indexXys;
- indexRgbas = indexes.indexRgbas;
- indexLengthSoFar = indexes.indexLengthSoFar;
- }
+ };
+}
+
+export function newEventDashedBordersPainterFactory(barOpts?: TimelineEventBarsPainterOptions): TimelineEventsPainterFactory {
+
+ // Painter Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions): Painter {
+
+ const helper = eventDashedBorderPainterHelper(barOpts, drawable, timeAxis, lanes, ui, options);
+
+ // Painter
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ helper.ensureCapacity(lanes.numEvents);
+
+ let indexXys = 0;
+ let indexRgbas = 0;
+ let indexLengthSoFar = 0;
+
+ for (let l = 0; l < lanes.length; l++) {
+ const lane = lanes.lane(l);
+ for (let e = 0; e < lane.length; e++) {
+ const event = lane.event(e);
+ const indexes = helper.fillEvent(l, e, indexXys, indexRgbas, viewport, indexLengthSoFar);
+ indexXys = indexes.indexXys;
+ indexRgbas = indexes.indexRgbas;
+ indexLengthSoFar = indexes.indexLengthSoFar;
}
+ }
- helper.paint( indexXys, indexRgbas, gl, viewport, indexLengthSoFar );
- };
+ helper.paint(indexXys, indexRgbas, gl, viewport, indexLengthSoFar);
};
- }
-
+ };
+}
- export interface TimelineEventIconsPainterOptions {
- topMargin? : number;
- bottomMargin? : number;
- vAlign? : number;
- }
-
- function eventIconsPainterHelper( iconOpts : TimelineEventIconsPainterOptions, drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) {
-
- var rowTopPadding = options.rowTopPadding;
- var rowBottomPadding = options.rowBottomPadding;
- var laneHeight = options.laneHeight;
-
- var topMargin = ( hasval( iconOpts ) && hasval( iconOpts.topMargin ) ? iconOpts.topMargin : 1.2 );
- var bottomMargin = ( hasval( iconOpts ) && hasval( iconOpts.bottomMargin ) ? iconOpts.bottomMargin : 1.2 );
- var vAlign = ( hasval( iconOpts ) && hasval( iconOpts.vAlign ) ? iconOpts.vAlign : 0.5 );
-
- var textureRenderer = new TextureRenderer( );
-
- return {
- textureRenderer : textureRenderer,
- paintEvent: function( laneIndex : number, eventIndex : number, gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- var lane : TimelineLane = lanes.lane( laneIndex );
- var event : TimelineEventModel = lane.event( eventIndex );
- var eventStyle = ui.eventStyle( event.styleGuid );
-
- var jTop = rowTopPadding + ( laneIndex )*laneHeight + topMargin;
- var yFrac = ( viewport.h - jTop - ( 1.0 - vAlign )*( laneHeight - topMargin - bottomMargin ) ) / viewport.h;
-
- for ( var n = 0; n < eventStyle.numIcons; n++ ) {
- var icon = eventStyle.icon( n );
- var iconTime_PMILLIS = event.start_PMILLIS + icon.hPos*( event.end_PMILLIS - event.start_PMILLIS );
- var xFrac = timeAxis.tFrac( iconTime_PMILLIS );
- var w = icon.displayWidth / viewport.w;
- if ( -w <= xFrac && xFrac <= 1+w ) {
- var iconTexture = ui.loadImage( icon.url, function( ) { drawable.redraw( ); } );
- if ( iconTexture ) {
- textureRenderer.draw( gl, iconTexture, xFrac, yFrac, { xAnchor: icon.hAlign, yAnchor: vAlign, width: icon.displayWidth, height: icon.displayHeight } );
- }
+export interface TimelineEventIconsPainterOptions {
+ topMargin?: number;
+ bottomMargin?: number;
+ vAlign?: number;
+}
+
+
+function eventIconsPainterHelper(iconOpts: TimelineEventIconsPainterOptions, drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions) {
+
+ const rowTopPadding = options.rowTopPadding;
+ const rowBottomPadding = options.rowBottomPadding;
+ const laneHeight = options.laneHeight;
+
+ const topMargin = (hasval(iconOpts) && hasval(iconOpts.topMargin) ? iconOpts.topMargin : 1.2);
+ const bottomMargin = (hasval(iconOpts) && hasval(iconOpts.bottomMargin) ? iconOpts.bottomMargin : 1.2);
+ const vAlign = (hasval(iconOpts) && hasval(iconOpts.vAlign) ? iconOpts.vAlign : 0.5);
+
+ const textureRenderer = new TextureRenderer();
+
+ return {
+ textureRenderer: textureRenderer,
+ paintEvent: function (laneIndex: number, eventIndex: number, gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ const lane: TimelineLane = lanes.lane(laneIndex);
+ const event: TimelineEventModel = lane.event(eventIndex);
+ const eventStyle = ui.eventStyle(event.styleGuid);
+
+ const jTop = rowTopPadding + (laneIndex) * laneHeight + topMargin;
+ const yFrac = (viewport.h - jTop - (1.0 - vAlign) * (laneHeight - topMargin - bottomMargin)) / viewport.h;
+
+ for (let n = 0; n < eventStyle.numIcons; n++) {
+ const icon = eventStyle.icon(n);
+ const iconTime_PMILLIS = event.start_PMILLIS + icon.hPos * (event.end_PMILLIS - event.start_PMILLIS);
+ const xFrac = timeAxis.tFrac(iconTime_PMILLIS);
+ const w = icon.displayWidth / viewport.w;
+ if (-w <= xFrac && xFrac <= 1 + w) {
+ const iconTexture = ui.loadImage(icon.url, function () { drawable.redraw(); });
+ if (iconTexture) {
+ textureRenderer.draw(gl, iconTexture, xFrac, yFrac, { xAnchor: icon.hAlign, yAnchor: vAlign, width: icon.displayWidth, height: icon.displayHeight });
}
}
}
- };
- }
+ }
+ };
+}
- export function newEventIconsPainterFactory( iconOpts? : TimelineEventIconsPainterOptions ) : TimelineEventsPainterFactory {
+export function newEventIconsPainterFactory(iconOpts?: TimelineEventIconsPainterOptions): TimelineEventsPainterFactory {
- // Painter Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) : Painter {
-
- var helper = eventIconsPainterHelper( iconOpts, drawable, timeAxis, lanes, ui, options );
+ // Painter Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions): Painter {
- // Painter
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
+ const helper = eventIconsPainterHelper(iconOpts, drawable, timeAxis, lanes, ui, options);
- helper.textureRenderer.begin( gl, viewport );
- for ( var l = 0; l < lanes.length; l++ ) {
- var lane = lanes.lane( l );
- for ( var e = 0; e < lane.length; e++ ) {
- helper.paintEvent( l, e, gl, viewport );
- }
+ // Painter
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ helper.textureRenderer.begin(gl, viewport);
+ for (let l = 0; l < lanes.length; l++) {
+ const lane = lanes.lane(l);
+ for (let e = 0; e < lane.length; e++) {
+ helper.paintEvent(l, e, gl, viewport);
}
- helper.textureRenderer.end( gl );
- };
+ }
+ helper.textureRenderer.end(gl);
};
- }
+ };
+}
- export interface TimelineEventLabelOptions {
- topMargin? : number;
- bottomMargin? : number;
- leftMargin? : number;
- rightMargin? : number;
- vAlign? : number;
- spacing? : number;
- extendBeyondBar?: boolean;
-
- // Options:
- // 'force' always show text regardless of available space
- // 'truncate' truncate text with '...' when space is insufficient
- // 'show' show text if space exits, hide all the text if it cannot be displayed in its entirity
- textMode? : string;
-
- iconsEnabled? : boolean;
- // Can be a number, or 'imageSize', or 'auto'
- iconsForceWidth? : any;
- // Can be a number, or 'imageSize', or 'auto'
- iconsForceHeight? : any;
- iconsSizeFactor? : number;
-
- textEnabled? : boolean;
- textDefaultColor? : Color;
- textFont? : string;
- }
+export interface TimelineEventLabelOptions {
+ topMargin?: number;
+ bottomMargin?: number;
+ leftMargin?: number;
+ rightMargin?: number;
+ vAlign?: number;
+ spacing?: number;
+ extendBeyondBar?: boolean;
+
+ // Options:
+ // 'force' always show text regardless of available space
+ // 'truncate' truncate text with '...' when space is insufficient
+ // 'show' show text if space exits, hide all the text if it cannot be displayed in its entirity
+ textMode?: string;
+
+ iconsEnabled?: boolean;
+ // Can be a number, or 'imageSize', or 'auto'
+ iconsForceWidth?: any;
+ // Can be a number, or 'imageSize', or 'auto'
+ iconsForceHeight?: any;
+ iconsSizeFactor?: number;
+
+ textEnabled?: boolean;
+ textDefaultColor?: Color;
+ textFont?: string;
+}
- function calculateTextWidth(textEnabled: boolean, labelText: string, fgColor: Color, textDefaultColor: Color,
- textTextures: TwoKeyCache, viewport: BoundsUnmodifiable) {
- var wText = 0;
- var textTexture;
- if (textEnabled && labelText) {
- var textColor = Webglimpse.hasval(fgColor) ? fgColor : textDefaultColor;
- textTexture = textTextures.value(textColor.rgbaString, labelText);
- wText = textTexture.w / viewport.w;
- }
- return {
- wText: wText,
- textTexture: textTexture
- };
+function calculateTextWidth(textEnabled: boolean, labelText: string, fgColor: Color, textDefaultColor: Color,
+ textTextures: TwoKeyCache, viewport: BoundsUnmodifiable) {
+ let wText = 0;
+ let textTexture;
+ if (textEnabled && labelText) {
+ const textColor = hasval(fgColor) ? fgColor : textDefaultColor;
+ textTexture = textTextures.value(textColor.rgbaString, labelText);
+ wText = textTexture.w / viewport.w;
}
+ return {
+ wText: wText,
+ textTexture: textTexture
+ };
+}
- function eventLabelsPainterHelper( labelOpts : TimelineEventLabelOptions, drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) {
- var rowTopPadding = options.rowTopPadding;
- var rowBottomPadding = options.rowBottomPadding;
- var laneHeight = options.laneHeight;
-
- var topMargin = ( hasval( labelOpts ) && hasval( labelOpts.topMargin ) ? labelOpts.topMargin : 1.2 );
- var bottomMargin = ( hasval( labelOpts ) && hasval( labelOpts.bottomMargin ) ? labelOpts.bottomMargin : 1.2 );
- var leftMargin = ( hasval( labelOpts ) && hasval( labelOpts.leftMargin ) ? labelOpts.leftMargin : 4 );
- var rightMargin = ( hasval( labelOpts ) && hasval( labelOpts.rightMargin ) ? labelOpts.rightMargin : 4 );
- var vAlign = ( hasval( labelOpts ) && hasval( labelOpts.vAlign ) ? labelOpts.vAlign : 0.5 );
- var spacing = ( hasval( labelOpts ) && hasval( labelOpts.spacing ) ? labelOpts.spacing : 3 );
- var extendBeyondBar = ( hasval( labelOpts ) && hasval( labelOpts.extendBeyondBar ) ? labelOpts.extendBeyondBar : false );
- var textMode = ( hasval( labelOpts ) && hasval( labelOpts.textMode ) ? labelOpts.textMode : 'force' );
-
- // Icon options
- var iconsEnabled = ( hasval( labelOpts ) && hasval( labelOpts.iconsEnabled ) ? labelOpts.iconsEnabled : true );
- var iconsForceWidth = ( hasval( labelOpts ) && hasval( labelOpts.iconsForceWidth ) ? labelOpts.iconsForceWidth : 'auto' );
- var iconsForceHeight = ( hasval( labelOpts ) && hasval( labelOpts.iconsForceHeight ) ? labelOpts.iconsForceHeight : 'auto' );
- var iconsSizeFactor = ( hasval( labelOpts ) && hasval( labelOpts.iconsSizeFactor ) ? labelOpts.iconsSizeFactor : 1 );
-
- // Text options
- var textEnabled = ( hasval( labelOpts ) && hasval( labelOpts.textEnabled ) ? labelOpts.textEnabled : true );
- var textDefaultColor = ( hasval( labelOpts ) && hasval( labelOpts.textDefaultColor ) ? labelOpts.textDefaultColor : options.timelineFgColor );
- var textFont = ( hasval( labelOpts ) && hasval( labelOpts.textFont ) ? labelOpts.textFont : options.timelineFont );
-
- // XXX: Old icon textures never get cleaned out
- var iconTextures : StringMap = {};
- var textTextures = newTextTextureCache2( textFont );
- var textureRenderer = new TextureRenderer( );
-
- return {
- textTextures : textTextures,
- textureRenderer : textureRenderer,
- paintEvent: function( laneIndex : number, eventIndex : number, gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
-
- var lane : TimelineLane = lanes.lane( laneIndex );
- var event : TimelineEventModel = lane.event( eventIndex );
-
- var labelTopMargin = hasval( event.labelTopMargin ) ? event.labelTopMargin : topMargin;
- var labelBottomMargin = hasval( event.labelBottomMargin ) ? event.labelBottomMargin : bottomMargin;
-
- var labelVAlign = hasval( event.labelVAlign ) ? event.labelVAlign : vAlign;
- var labelVPos = hasval( event.labelVPos ) ? event.labelVPos : labelVAlign;
-
- var labelHAlign = hasval( event.labelHAlign ) ? event.labelHAlign : 0;
- var labelHPos = hasval( event.labelHPos ) ? event.labelHPos : labelHAlign;
-
- var jTop = rowTopPadding + ( laneIndex )*laneHeight + labelTopMargin;
- var yFrac = ( viewport.h - jTop - ( 1.0 - labelVAlign )*( laneHeight - labelTopMargin - labelBottomMargin ) ) / viewport.h;
-
- var xLeftMin = 2 / viewport.w;
- var xRightMax = ( viewport.w - 2 ) / viewport.w;
- var wLeftIndent = leftMargin / viewport.w;
- var wRightIndent = rightMargin / viewport.w;
-
- var xStart = timeAxis.tFrac( event.start_PMILLIS );
- var xEnd = timeAxis.tFrac( event.end_PMILLIS );
-
- var wTotal = ( xEnd - wRightIndent ) - ( xStart + wLeftIndent )
- var wSpacing = ( spacing / viewport.w );
-
- if ( !( xEnd <= 0 || xStart > 1 ) ) {
-
- var xLeft;
- var xRight;
- if ( extendBeyondBar ) {
- if ( eventIndex+1 < lane.length ) {
- var nextEvent = lane.event( eventIndex+1 );
- var nextStart_PMILLIS = effectiveEdges_PMILLIS( ui, nextEvent )[ 0 ];
- xRight = timeAxis.tFrac( nextStart_PMILLIS );
- }
- else {
- xRight = xRightMax;
- }
-
- if ( eventIndex-1 >= 0 ) {
- var previousEvent = lane.event( eventIndex-1 );
- var previousEnd_PMILLIS = effectiveEdges_PMILLIS( ui, previousEvent )[ 1 ];
- xLeft = timeAxis.tFrac( previousEnd_PMILLIS );
- }
- else {
- xLeft = xLeftMin;
- }
+function eventLabelsPainterHelper(labelOpts: TimelineEventLabelOptions, drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions) {
+ const rowTopPadding = options.rowTopPadding;
+ const rowBottomPadding = options.rowBottomPadding;
+ const laneHeight = options.laneHeight;
+
+ const topMargin = (hasval(labelOpts) && hasval(labelOpts.topMargin) ? labelOpts.topMargin : 1.2);
+ const bottomMargin = (hasval(labelOpts) && hasval(labelOpts.bottomMargin) ? labelOpts.bottomMargin : 1.2);
+ const leftMargin = (hasval(labelOpts) && hasval(labelOpts.leftMargin) ? labelOpts.leftMargin : 4);
+ const rightMargin = (hasval(labelOpts) && hasval(labelOpts.rightMargin) ? labelOpts.rightMargin : 4);
+ const vAlign = (hasval(labelOpts) && hasval(labelOpts.vAlign) ? labelOpts.vAlign : 0.5);
+ const spacing = (hasval(labelOpts) && hasval(labelOpts.spacing) ? labelOpts.spacing : 3);
+ const extendBeyondBar = (hasval(labelOpts) && hasval(labelOpts.extendBeyondBar) ? labelOpts.extendBeyondBar : false);
+ const textMode = (hasval(labelOpts) && hasval(labelOpts.textMode) ? labelOpts.textMode : 'force');
+
+ // Icon options
+ const iconsEnabled = (hasval(labelOpts) && hasval(labelOpts.iconsEnabled) ? labelOpts.iconsEnabled : true);
+ const iconsForceWidth = (hasval(labelOpts) && hasval(labelOpts.iconsForceWidth) ? labelOpts.iconsForceWidth : 'auto');
+ const iconsForceHeight = (hasval(labelOpts) && hasval(labelOpts.iconsForceHeight) ? labelOpts.iconsForceHeight : 'auto');
+ const iconsSizeFactor = (hasval(labelOpts) && hasval(labelOpts.iconsSizeFactor) ? labelOpts.iconsSizeFactor : 1);
+
+ // Text options
+ const textEnabled = (hasval(labelOpts) && hasval(labelOpts.textEnabled) ? labelOpts.textEnabled : true);
+ const textDefaultColor = (hasval(labelOpts) && hasval(labelOpts.textDefaultColor) ? labelOpts.textDefaultColor : options.timelineFgColor);
+ const textFont = (hasval(labelOpts) && hasval(labelOpts.textFont) ? labelOpts.textFont : options.timelineFont);
+
+ // XXX: Old icon textures never get cleaned out
+ const iconTextures: StringMap = {};
+ const textTextures = newTextTextureCache2(textFont);
+ const textureRenderer = new TextureRenderer();
+
+ return {
+ textTextures: textTextures,
+ textureRenderer: textureRenderer,
+ paintEvent: function (laneIndex: number, eventIndex: number, gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+
+ const lane: TimelineLane = lanes.lane(laneIndex);
+ const event: TimelineEventModel = lane.event(eventIndex);
+
+ const labelTopMargin = hasval(event.labelTopMargin) ? event.labelTopMargin : topMargin;
+ const labelBottomMargin = hasval(event.labelBottomMargin) ? event.labelBottomMargin : bottomMargin;
+
+ const labelVAlign = hasval(event.labelVAlign) ? event.labelVAlign : vAlign;
+ const labelVPos = hasval(event.labelVPos) ? event.labelVPos : labelVAlign;
+
+ const labelHAlign = hasval(event.labelHAlign) ? event.labelHAlign : 0;
+ const labelHPos = hasval(event.labelHPos) ? event.labelHPos : labelHAlign;
+
+ const jTop = rowTopPadding + (laneIndex) * laneHeight + labelTopMargin;
+ const yFrac = (viewport.h - jTop - (1.0 - labelVAlign) * (laneHeight - labelTopMargin - labelBottomMargin)) / viewport.h;
+
+ const xLeftMin = 2 / viewport.w;
+ const xRightMax = (viewport.w - 2) / viewport.w;
+ const wLeftIndent = leftMargin / viewport.w;
+ const wRightIndent = rightMargin / viewport.w;
+
+ const xStart = timeAxis.tFrac(event.start_PMILLIS);
+ const xEnd = timeAxis.tFrac(event.end_PMILLIS);
+
+ const wTotal = (xEnd - wRightIndent) - (xStart + wLeftIndent);
+ const wSpacing = (spacing / viewport.w);
+
+ if (!(xEnd <= 0 || xStart > 1)) {
+
+ let xLeft;
+ let xRight;
+ if (extendBeyondBar) {
+ if (eventIndex + 1 < lane.length) {
+ const nextEvent = lane.event(eventIndex + 1);
+ const nextStart_PMILLIS = effectiveEdges_PMILLIS(ui, nextEvent)[0];
+ xRight = timeAxis.tFrac(nextStart_PMILLIS);
}
else {
- xRight = xEnd;
- xLeft = xStart;
- }
-
- // calculate Text width
- var calculatedTextWidth = calculateTextWidth(textEnabled, event.label, event.fgColor, textDefaultColor, textTextures, viewport);
- var wText = calculatedTextWidth.wText;
- var textTexture = calculatedTextWidth.textTexture;
-
- // calculate Icon width (and start load if necessary)
- var wIcon = 0;
- var wIconPlusSpacing = 0;
- var iconWidth;
- var iconHeight;
- var iconTexture;
- if ( iconsEnabled && event.labelIcon ) {
- iconTexture = iconTextures[ event.labelIcon ];
- if ( hasval( iconTexture ) ) {
- iconWidth = ( isNumber( iconsForceWidth ) ? iconsForceWidth : ( iconsForceWidth === 'imageSize' ? iconTexture.w : null ) );
- iconHeight = ( isNumber( iconsForceHeight ) ? iconsForceHeight : ( iconsForceHeight === 'imageSize' ? iconTexture.h : null ) );
-
- var wIconKnown = hasval( iconWidth );
- var hIconKnown = hasval( iconHeight );
- if ( !wIconKnown && !hIconKnown ) {
- iconHeight = Math.round( iconsSizeFactor * ( laneHeight - labelTopMargin - labelBottomMargin ) );
- iconWidth = iconTexture.w * iconHeight / iconTexture.h;
- }
- else if ( !wIconKnown ) {
- iconHeight = Math.round( iconsSizeFactor * iconHeight );
- iconWidth = iconTexture.w * iconHeight / iconTexture.h;
- }
- else if ( !hIconKnown ) {
- iconWidth = Math.round( iconsSizeFactor * iconWidth );
- iconHeight = iconTexture.h * iconWidth / iconTexture.w;
- }
- else {
- iconWidth = Math.round( iconsSizeFactor * iconWidth );
- iconHeight = Math.round( iconsSizeFactor * iconHeight );
- }
-
- wIcon = ( iconWidth / viewport.w );
-
- wIconPlusSpacing = wIcon + wSpacing;
- }
- // A null in the map means a fetch has already been initiated
- // ... either it is still in progress, or it has already failed
- else if ( iconTexture !== null ) {
- iconTextures[ event.labelIcon ] = null;
-
- var image = new Image( );
- image.onload = ( function( url, img ) {
- return function( ) {
- var wImage = img.naturalWidth;
- var hImage = img.naturalHeight;
- iconTextures[ url ] = new Texture2D( wImage, hImage, GL.LINEAR, GL.LINEAR, function( g ) {
- g.drawImage( img, 0, 0 );
- } );
- drawable.redraw( );
- };
- } )( event.labelIcon, image );
- image.src = event.labelIcon;
- }
+ xRight = xRightMax;
}
-
- // NOTE: With extendBeyondBar=true, we detect when there is insufficient space between the current event
- // and those to either side to display the text + icon. However, if one event has right aligned text
- // and the other has left aligned text, so both text labels overlap into the same space between the
- // events, we don't currently try to detect that.
-
- // Determine whether there is enough space to display both text and icon, or only icon, or neither
- // coordinates of the start edge of the icon + label
- var xStartLabel = xStart + wLeftIndent - ( wSpacing + wIcon + wText ) * labelHPos + ( wTotal ) * labelHAlign;
- // coordinates of the end edge of the icon + label
- var xEndLabel = xStartLabel + ( wSpacing + wIcon + wText );
-
- // adjust xStartLabel and xEndLabel if they fall off the screen
- if ( xStartLabel < xLeftMin ) {
- xStartLabel = xLeftMin;
- xEndLabel = xStartLabel + ( wSpacing + wIcon + wText );
+ if (eventIndex - 1 >= 0) {
+ const previousEvent = lane.event(eventIndex - 1);
+ const previousEnd_PMILLIS = effectiveEdges_PMILLIS(ui, previousEvent)[1];
+ xLeft = timeAxis.tFrac(previousEnd_PMILLIS);
}
- else if ( xEndLabel > xRightMax ) {
- xEndLabel = xRightMax;
- xStartLabel = xEndLabel - ( wSpacing + wIcon + wText );
+ else {
+ xLeft = xLeftMin;
}
-
- if (textMode === 'truncate') {
- var labelText = event.label;
- while (!!labelText && labelText !== "...") {
- if (xEndLabel > xRight || xStartLabel < xLeft) {
- // there is not enough room for the text, begin truncating the text
- labelText = labelText.substring(0, labelText.length - 4).concat("...");
- var calculatedTextWidth = calculateTextWidth(textEnabled, labelText, event.fgColor, textDefaultColor, textTextures, viewport);
- wText = calculatedTextWidth.wText;
- textTexture = calculatedTextWidth.textTexture;
-
- xStartLabel = xStart + wLeftIndent - (wSpacing + wIcon + wText) * labelHPos + wTotal * labelHAlign;
- // coordinates of the end edge of the icon + label
- xEndLabel = xStartLabel + (wSpacing + wIcon + wText);
- // adjust xStartLabel and xEndLabel if they fall off the screen
- if (xStartLabel < xLeftMin) {
- xStartLabel = xLeftMin;
- xEndLabel = xStartLabel + (wSpacing + wIcon + wText);
- } else if (xEndLabel > xRightMax) {
- xEndLabel = xRightMax;
- xStartLabel = xEndLabel - (wSpacing + wIcon + wText);
- }
- } else {
- break;
- }
+ }
+ else {
+ xRight = xEnd;
+ xLeft = xStart;
+ }
+
+ // calculate Text width
+ let calculatedTextWidth = calculateTextWidth(textEnabled, event.label, event.fgColor, textDefaultColor, textTextures, viewport);
+ let wText = calculatedTextWidth.wText;
+ let textTexture = calculatedTextWidth.textTexture;
+
+ // calculate Icon width (and start load if necessary)
+ let wIcon = 0;
+ let wIconPlusSpacing = 0;
+ let iconWidth;
+ let iconHeight;
+ let iconTexture;
+ if (iconsEnabled && event.labelIcon) {
+ iconTexture = iconTextures[event.labelIcon];
+ if (hasval(iconTexture)) {
+ iconWidth = (isNumber(iconsForceWidth) ? iconsForceWidth : (iconsForceWidth === 'imageSize' ? iconTexture.w : null));
+ iconHeight = (isNumber(iconsForceHeight) ? iconsForceHeight : (iconsForceHeight === 'imageSize' ? iconTexture.h : null));
+
+ const wIconKnown = hasval(iconWidth);
+ const hIconKnown = hasval(iconHeight);
+ if (!wIconKnown && !hIconKnown) {
+ iconHeight = Math.round(iconsSizeFactor * (laneHeight - labelTopMargin - labelBottomMargin));
+ iconWidth = iconTexture.w * iconHeight / iconTexture.h;
+ }
+ else if (!wIconKnown) {
+ iconHeight = Math.round(iconsSizeFactor * iconHeight);
+ iconWidth = iconTexture.w * iconHeight / iconTexture.h;
+ }
+ else if (!hIconKnown) {
+ iconWidth = Math.round(iconsSizeFactor * iconWidth);
+ iconHeight = iconTexture.h * iconWidth / iconTexture.w;
}
- if (!labelText || labelText === "...") {
- wText = 0;
- textTexture = null;
+ else {
+ iconWidth = Math.round(iconsSizeFactor * iconWidth);
+ iconHeight = Math.round(iconsSizeFactor * iconHeight);
}
- } else if (textMode === 'show') {
- if ( xEndLabel > xRight || xStartLabel < xLeft ) {
- // there is not enough room for the text, try with just the icon
- wText = 0;
- textTexture = null;
-
- // coordinates of the start edge of the icon + label
- var xStartLabel = xStart + wLeftIndent - ( wIcon ) * labelHPos + ( wTotal ) * labelHAlign;
+
+ wIcon = (iconWidth / viewport.w);
+
+ wIconPlusSpacing = wIcon + wSpacing;
+ }
+ // A null in the map means a fetch has already been initiated
+ // ... either it is still in progress, or it has already failed
+ else if (iconTexture !== null) {
+ iconTextures[event.labelIcon] = null;
+
+ const image = new Image();
+ image.onload = (function (url, img) {
+ return function () {
+ const wImage = img.naturalWidth;
+ const hImage = img.naturalHeight;
+ iconTextures[url] = new Texture2D(wImage, hImage, GL.LINEAR, GL.LINEAR, function (g) {
+ g.drawImage(img, 0, 0);
+ });
+ drawable.redraw();
+ };
+ })(event.labelIcon, image);
+ image.src = event.labelIcon;
+ }
+ }
+
+ // NOTE: With extendBeyondBar=true, we detect when there is insufficient space between the current event
+ // and those to either side to display the text + icon. However, if one event has right aligned text
+ // and the other has left aligned text, so both text labels overlap into the same space between the
+ // events, we don't currently try to detect that.
+
+ // Determine whether there is enough space to display both text and icon, or only icon, or neither
+
+ // coordinates of the start edge of the icon + label
+ let xStartLabel = xStart + wLeftIndent - (wSpacing + wIcon + wText) * labelHPos + (wTotal) * labelHAlign;
+ // coordinates of the end edge of the icon + label
+ let xEndLabel = xStartLabel + (wSpacing + wIcon + wText);
+
+ // adjust xStartLabel and xEndLabel if they fall off the screen
+ if (xStartLabel < xLeftMin) {
+ xStartLabel = xLeftMin;
+ xEndLabel = xStartLabel + (wSpacing + wIcon + wText);
+ }
+ else if (xEndLabel > xRightMax) {
+ xEndLabel = xRightMax;
+ xStartLabel = xEndLabel - (wSpacing + wIcon + wText);
+ }
+
+ if (textMode === 'truncate') {
+ let labelText = event.label;
+ while (!!labelText && labelText !== '...') {
+ if (xEndLabel > xRight || xStartLabel < xLeft) {
+ // there is not enough room for the text, begin truncating the text
+ labelText = labelText.substring(0, labelText.length - 4).concat('...');
+ calculatedTextWidth = calculateTextWidth(textEnabled, labelText, event.fgColor, textDefaultColor, textTextures, viewport);
+ wText = calculatedTextWidth.wText;
+ textTexture = calculatedTextWidth.textTexture;
+
+ xStartLabel = xStart + wLeftIndent - (wSpacing + wIcon + wText) * labelHPos + wTotal * labelHAlign;
// coordinates of the end edge of the icon + label
- var xEndLabel = xStartLabel + ( wIcon );
-
+ xEndLabel = xStartLabel + (wSpacing + wIcon + wText);
// adjust xStartLabel and xEndLabel if they fall off the screen
- if ( xStartLabel < xLeftMin ) {
+ if (xStartLabel < xLeftMin) {
xStartLabel = xLeftMin;
- xEndLabel = xStartLabel + ( wIcon );
- }
- else if ( xEndLabel > xRightMax ) {
+ xEndLabel = xStartLabel + (wSpacing + wIcon + wText);
+ } else if (xEndLabel > xRightMax) {
xEndLabel = xRightMax;
- xStartLabel = xEndLabel - ( wIcon );
- }
-
- // if there is still not enough room, don't show anything
- if ( xEndLabel > xRight || xStartLabel < xLeft ) {
- wIcon = 0;
- iconTexture = null;
+ xStartLabel = xEndLabel - (wSpacing + wIcon + wText);
}
+ } else {
+ break;
}
}
-
- // Icons
- if ( hasval( iconTexture ) ) {
- // coordinates of the start edge of the icon + label
- var xStartLabel = xStart + wLeftIndent - ( wSpacing + wIcon + wText ) * labelHPos + ( wTotal ) * labelHAlign;
-
- // coordinates of the end edge of the icon + label
- var xEndLabel = xStartLabel + ( wSpacing + wIcon + wText );
-
- if ( xStartLabel < xLeftMin ) {
- textureRenderer.draw( gl, iconTexture, xLeftMin, yFrac, { xAnchor: 0, yAnchor: labelVPos, width: iconWidth, height: iconHeight } );
- }
- else if ( xEndLabel > xRightMax ) {
- textureRenderer.draw( gl, iconTexture, xRightMax - wSpacing - wText, yFrac, { xAnchor: 1, yAnchor: labelVPos, width: iconWidth, height: iconHeight } );
- }
- else {
- var xFrac = xStart + wLeftIndent - ( wSpacing + wText ) * labelHPos + ( wTotal ) * labelHAlign;
- textureRenderer.draw( gl, iconTexture, xFrac, yFrac, { xAnchor: labelHPos, yAnchor: labelVPos, width: iconWidth, height: iconHeight } );
- }
+ if (!labelText || labelText === '...') {
+ wText = 0;
+ textTexture = null;
}
-
- // Text
- if ( hasval( textTexture ) ) {
+ } else if (textMode === 'show') {
+ if (xEndLabel > xRight || xStartLabel < xLeft) {
+ // there is not enough room for the text, try with just the icon
+ wText = 0;
+ textTexture = null;
+
// coordinates of the start edge of the icon + label
- var xStartLabel = xStart + wLeftIndent - ( wSpacing + wIcon + wText ) * labelHPos + ( wTotal ) * labelHAlign;
-
+ xStartLabel = xStart + wLeftIndent - (wIcon) * labelHPos + (wTotal) * labelHAlign;
// coordinates of the end edge of the icon + label
- var xEndLabel = xStartLabel + ( wSpacing + wIcon + wText );
-
- if ( xStartLabel < xLeftMin ) {
- textureRenderer.draw( gl, textTexture, xLeftMin + wSpacing + wIcon, yFrac, { xAnchor: 0, yAnchor: textTexture.yAnchor( labelVPos ) } );
+ xEndLabel = xStartLabel + (wIcon);
+
+ // adjust xStartLabel and xEndLabel if they fall off the screen
+ if (xStartLabel < xLeftMin) {
+ xStartLabel = xLeftMin;
+ xEndLabel = xStartLabel + (wIcon);
}
- else if ( xEndLabel > xRightMax ) {
- textureRenderer.draw( gl, textTexture, xRightMax, yFrac, { xAnchor: 1, yAnchor: textTexture.yAnchor( labelVPos ) } );
+ else if (xEndLabel > xRightMax) {
+ xEndLabel = xRightMax;
+ xStartLabel = xEndLabel - (wIcon);
}
- else {
- var xFrac = xStart + wLeftIndent + ( wIconPlusSpacing ) * ( 1 - labelHPos ) + ( wTotal ) * labelHAlign;
- textureRenderer.draw( gl, textTexture, xFrac, yFrac, { xAnchor: labelHPos, yAnchor: textTexture.yAnchor( labelVPos ) } );
+
+ // if there is still not enough room, don't show anything
+ if (xEndLabel > xRight || xStartLabel < xLeft) {
+ wIcon = 0;
+ iconTexture = null;
}
}
}
- }
- };
- }
-
- export function newEventLabelsPainterFactory( labelOpts? : TimelineEventLabelOptions ) : TimelineEventsPainterFactory {
- // Painter Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) : Painter {
+ // Icons
+ if (hasval(iconTexture)) {
+ // coordinates of the start edge of the icon + label
+ xStartLabel = xStart + wLeftIndent - (wSpacing + wIcon + wText) * labelHPos + (wTotal) * labelHAlign;
- var helper = eventLabelsPainterHelper( labelOpts, drawable, timeAxis, lanes, ui, options );
-
- // Painter
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
+ // coordinates of the end edge of the icon + label
+ xEndLabel = xStartLabel + (wSpacing + wIcon + wText);
- helper.textTextures.resetTouches( );
- helper.textureRenderer.begin( gl, viewport );
- for ( var l = 0; l < lanes.length; l++ ) {
- var lane : TimelineLane = lanes.lane( l );
- for ( var e = 0; e < lane.length; e++ ) {
- helper.paintEvent( l, e, gl, viewport );
+ if (xStartLabel < xLeftMin) {
+ textureRenderer.draw(gl, iconTexture, xLeftMin, yFrac, { xAnchor: 0, yAnchor: labelVPos, width: iconWidth, height: iconHeight });
+ }
+ else if (xEndLabel > xRightMax) {
+ textureRenderer.draw(gl, iconTexture, xRightMax - wSpacing - wText, yFrac, { xAnchor: 1, yAnchor: labelVPos, width: iconWidth, height: iconHeight });
+ }
+ else {
+ const xFrac = xStart + wLeftIndent - (wSpacing + wText) * labelHPos + (wTotal) * labelHAlign;
+ textureRenderer.draw(gl, iconTexture, xFrac, yFrac, { xAnchor: labelHPos, yAnchor: labelVPos, width: iconWidth, height: iconHeight });
}
}
- helper.textureRenderer.end( gl );
- helper.textTextures.retainTouched( );
- };
- };
- }
+ // Text
+ if (hasval(textTexture)) {
+ // coordinates of the start edge of the icon + label
+ xStartLabel = xStart + wLeftIndent - (wSpacing + wIcon + wText) * labelHPos + (wTotal) * labelHAlign;
- function eventBarPainterHelper( barOpts : TimelineEventBarsPainterOptions, drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) {
- var rowTopPadding = options.rowTopPadding;
- var rowBottomPadding = options.rowBottomPadding;
- var laneHeight = options.laneHeight;
-
- var topMargin = ( hasval( barOpts ) && hasval( barOpts.topMargin ) ? barOpts.topMargin : 1.2 );
- var bottomMargin = ( hasval( barOpts ) && hasval( barOpts.bottomMargin ) ? barOpts.bottomMargin : 1.2 );
- var borderThickness = ( hasval( barOpts ) && hasval( barOpts.borderThickness ) ? barOpts.borderThickness : 2 );
- var cornerType = ( hasval( barOpts ) && hasval( barOpts.cornerType ) ? barOpts.cornerType : JointType.BEVEL );
- var defaultColor = ( hasval( barOpts ) && hasval( barOpts.defaultColor ) ? barOpts.defaultColor : options.timelineFgColor.withAlphaTimes( 0.4 ) );
- var defaultBorderColor = ( hasval( barOpts ) && hasval( barOpts.defaultBorderColor ) ? barOpts.defaultBorderColor : null );
- var selectedBorderColor = ( hasval( barOpts ) && hasval( barOpts.selectedBorderColor ) ? barOpts.selectedBorderColor : options.timelineFgColor );
- var minimumVisibleWidth = ( hasval( barOpts ) && hasval( barOpts.minimumVisibleWidth ) ? barOpts.minimumVisibleWidth : 0 );
-
- var selection = ui.selection;
-
- var xyFrac_vColor_VERTSHADER = concatLines(
- ' ',
- ' attribute vec2 a_XyFrac; ',
- ' attribute vec4 a_Color; ',
- ' ',
- ' varying vec4 v_Color; ',
- ' ',
- ' void main( ) { ',
- ' gl_Position = vec4( ( -1.0 + 2.0*a_XyFrac ), 0.0, 1.0 ); ',
- ' v_Color = a_Color; ',
- ' } ',
- ' '
- );
-
- var program = new Program( xyFrac_vColor_VERTSHADER, varyingColor_FRAGSHADER );
- var a_XyFrac = new Attribute( program, 'a_XyFrac' );
- var a_Color = new Attribute( program, 'a_Color' );
-
- var xys = new Float32Array( 0 );
- var xysBuffer = newDynamicBuffer( );
-
- var rgbas = new Float32Array( 0 );
- var rgbasBuffer = newDynamicBuffer( );
-
- return {
- paint( indexXys : number, indexRgbas : number, gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- if ( indexXys == 0 || indexRgbas == 0 ) return;
-
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
-
- program.use( gl );
- xysBuffer.setData( xys.subarray( 0, indexXys ) );
- a_XyFrac.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
- rgbasBuffer.setData( rgbas.subarray( 0, indexRgbas ) );
- a_Color.setDataAndEnable( gl, rgbasBuffer, 4, GL.FLOAT );
-
- gl.drawArrays( GL.TRIANGLES, 0, Math.floor( indexXys / 2 ) );
-
- a_Color.disable( gl );
- a_XyFrac.disable( gl );
- program.endUse( gl );
- },
- ensureCapacity: function( eventCount : number ) {
- var numVertices;
- switch ( cornerType ) {
- case JointType.BEVEL:
- numVertices = ( 6*( 5 /*quads*/ ) + 3*( 4 /*triangles*/ ) )*eventCount;
- break;
-
- default:
- numVertices = ( 6*( 5 /*quads*/ ) )*eventCount;
- break;
- }
+ // coordinates of the end edge of the icon + label
+ xEndLabel = xStartLabel + (wSpacing + wIcon + wText);
- xys = ensureCapacityFloat32( xys, 2*numVertices );
- rgbas = ensureCapacityFloat32( rgbas, 4*numVertices );
- },
- fillEvent: function( laneIndex : number, eventIndex : number, indexXys : number, indexRgbas : number, viewport : BoundsUnmodifiable ) : { indexXys : number; indexRgbas : number } {
- var lane : TimelineLane = lanes.lane( laneIndex );
- var event : TimelineEventModel = lane.event( eventIndex );
-
- var wBorder = borderThickness / viewport.w;
- var hBorder = borderThickness / viewport.h;
-
- var _topMargin = hasval( event.topMargin ) ? event.topMargin : topMargin;
- var _bottomMargin = hasval( event.bottomMargin ) ? event.bottomMargin : bottomMargin;
-
- var jTop = rowTopPadding + ( laneIndex )*laneHeight + _topMargin;
- var yTop = ( viewport.h - jTop ) / viewport.h;
- var jBottom = rowTopPadding + ( laneIndex + 1 )*laneHeight - _bottomMargin;
- var yBottom = ( viewport.h - jBottom ) / viewport.h;
-
- var xLeft = timeAxis.tFrac( event.start_PMILLIS );
- var xRight = timeAxis.tFrac( event.end_PMILLIS );
-
- var xWidthPixels = viewport.w * ( xRight - xLeft );
-
- if ( !( xRight < 0 || xLeft > 1 ) && xWidthPixels > minimumVisibleWidth ) {
-
- // Fill
- var fillColor = ( event.bgColor || defaultColor );
- if ( event === selection.hoveredEvent.value ) {
- fillColor = darker( fillColor, 0.8 );
+ if (xStartLabel < xLeftMin) {
+ textureRenderer.draw(gl, textTexture, xLeftMin + wSpacing + wIcon, yFrac, { xAnchor: 0, yAnchor: textTexture.yAnchor(labelVPos) });
}
- indexXys = putQuadXys( xys, indexXys, xLeft+wBorder, xRight-wBorder, yTop-hBorder, yBottom+hBorder );
- indexRgbas = putQuadRgbas( rgbas, indexRgbas, fillColor );
-
- // Border
- var borderColor = ( event.borderColor || ( event.bgColor ? fillColor : null ) || defaultBorderColor || fillColor );
- if ( selection.selectedEvents.hasValue( event ) ) {
- borderColor = selectedBorderColor;
+ else if (xEndLabel > xRightMax) {
+ textureRenderer.draw(gl, textTexture, xRightMax, yFrac, { xAnchor: 1, yAnchor: textTexture.yAnchor(labelVPos) });
}
- if ( borderColor ) {
- switch ( cornerType ) {
- case JointType.BEVEL:
- // Quads
- indexXys = putQuadXys( xys, indexXys, xLeft, xLeft+wBorder, yTop-hBorder, yBottom+hBorder );
- indexXys = putQuadXys( xys, indexXys, xRight-wBorder, xRight, yTop-hBorder, yBottom+hBorder );
- indexXys = putQuadXys( xys, indexXys, xLeft+wBorder, xRight-wBorder, yTop, yTop-hBorder );
- indexXys = putQuadXys( xys, indexXys, xLeft+wBorder, xRight-wBorder, yBottom+hBorder, yBottom );
- indexRgbas = putRgbas( rgbas, indexRgbas, borderColor, 24 );
- // Triangles
- indexXys = putLowerLeftTriangleXys( xys, indexXys, xRight-wBorder, xRight, yTop, yTop-hBorder );
- indexXys = putUpperLeftTriangleXys( xys, indexXys, xRight-wBorder, xRight, yBottom+hBorder, yBottom );
- indexXys = putUpperRightTriangleXys( xys, indexXys, xLeft, xLeft+wBorder, yBottom+hBorder, yBottom );
- indexXys = putLowerRightTriangleXys( xys, indexXys, xLeft, xLeft+wBorder, yTop, yTop-hBorder );
- indexRgbas = putRgbas( rgbas, indexRgbas, borderColor, 12 );
- break;
-
- default:
- indexXys = putQuadXys( xys, indexXys, xLeft, xRight-wBorder, yTop, yTop-hBorder );
- indexXys = putQuadXys( xys, indexXys, xRight-wBorder, xRight, yTop, yBottom+hBorder );
- indexXys = putQuadXys( xys, indexXys, xLeft+wBorder, xRight, yBottom+hBorder, yBottom );
- indexXys = putQuadXys( xys, indexXys, xLeft, xLeft+wBorder, yTop-hBorder, yBottom );
- indexRgbas = putRgbas( rgbas, indexRgbas, borderColor, 24 );
- break;
- }
+ else {
+ const xFrac = xStart + wLeftIndent + (wIconPlusSpacing) * (1 - labelHPos) + (wTotal) * labelHAlign;
+ textureRenderer.draw(gl, textTexture, xFrac, yFrac, { xAnchor: labelHPos, yAnchor: textTexture.yAnchor(labelVPos) });
}
}
-
- return { indexXys : indexXys, indexRgbas : indexRgbas };
}
+ }
+ };
+}
+
+export function newEventLabelsPainterFactory(labelOpts?: TimelineEventLabelOptions): TimelineEventsPainterFactory {
+
+ // Painter Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions): Painter {
+
+ const helper = eventLabelsPainterHelper(labelOpts, drawable, timeAxis, lanes, ui, options);
+
+ // Painter
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ helper.textTextures.resetTouches();
+ helper.textureRenderer.begin(gl, viewport);
+ for (let l = 0; l < lanes.length; l++) {
+ const lane: TimelineLane = lanes.lane(l);
+ for (let e = 0; e < lane.length; e++) {
+ helper.paintEvent(l, e, gl, viewport);
+ }
+ }
+ helper.textureRenderer.end(gl);
+ helper.textTextures.retainTouched();
};
- }
+ };
+}
- export function newEventBarsPainterFactory( barOpts? : TimelineEventBarsPainterOptions ) : TimelineEventsPainterFactory {
+function eventBarPainterHelper(barOpts: TimelineEventBarsPainterOptions, drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions) {
+ const rowTopPadding = options.rowTopPadding;
+ const rowBottomPadding = options.rowBottomPadding;
+ const laneHeight = options.laneHeight;
+
+ const topMargin = (hasval(barOpts) && hasval(barOpts.topMargin) ? barOpts.topMargin : 1.2);
+ const bottomMargin = (hasval(barOpts) && hasval(barOpts.bottomMargin) ? barOpts.bottomMargin : 1.2);
+ const borderThickness = (hasval(barOpts) && hasval(barOpts.borderThickness) ? barOpts.borderThickness : 2);
+ const cornerType = (hasval(barOpts) && hasval(barOpts.cornerType) ? barOpts.cornerType : JointType.BEVEL);
+ const defaultColor = (hasval(barOpts) && hasval(barOpts.defaultColor) ? barOpts.defaultColor : options.timelineFgColor.withAlphaTimes(0.4));
+ const defaultBorderColor = (hasval(barOpts) && hasval(barOpts.defaultBorderColor) ? barOpts.defaultBorderColor : null);
+ const selectedBorderColor = (hasval(barOpts) && hasval(barOpts.selectedBorderColor) ? barOpts.selectedBorderColor : null);
+ const minimumVisibleWidth = (hasval(barOpts) && hasval(barOpts.minimumVisibleWidth) ? barOpts.minimumVisibleWidth : 0);
+
+ const selection = ui.selection;
+
+ const xyFrac_vColor_VERTSHADER = concatLines(
+ ' ',
+ ' attribute vec2 a_XyFrac; ',
+ ' attribute vec4 a_Color; ',
+ ' ',
+ ' varying vec4 v_Color; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_Position = vec4( ( -1.0 + 2.0*a_XyFrac ), 0.0, 1.0 ); ',
+ ' v_Color = a_Color; ',
+ ' } ',
+ ' '
+ );
+
+ const program = new Program(xyFrac_vColor_VERTSHADER, varyingColor_FRAGSHADER);
+ const a_XyFrac = new Attribute(program, 'a_XyFrac');
+ const a_Color = new Attribute(program, 'a_Color');
+
+ let xys = new Float32Array(0);
+ const xysBuffer = newDynamicBuffer();
+
+ let rgbas = new Float32Array(0);
+ const rgbasBuffer = newDynamicBuffer();
+
+ return {
+ paint(indexXys: number, indexRgbas: number, gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ if (indexXys === 0 || indexRgbas === 0) {
+ return;
+ }
- // Painter Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) : Painter {
-
- var helper = eventBarPainterHelper( barOpts, drawable, timeAxis, lanes, ui, options );
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ program.use(gl);
+ xysBuffer.setData(xys.subarray(0, indexXys));
+ a_XyFrac.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+ rgbasBuffer.setData(rgbas.subarray(0, indexRgbas));
+ a_Color.setDataAndEnable(gl, rgbasBuffer, 4, GL.FLOAT);
+
+ gl.drawArrays(GL.TRIANGLES, 0, Math.floor(indexXys / 2));
+
+ a_Color.disable(gl);
+ a_XyFrac.disable(gl);
+ program.endUse(gl);
+ },
+ ensureCapacity: function (eventCount: number) {
+ let numVertices;
+ switch (cornerType) {
+ case JointType.BEVEL:
+ numVertices = (6 * (5 /*quads*/) + 3 * (4 /*triangles*/)) * eventCount;
+ break;
+
+ default:
+ numVertices = (6 * (5 /*quads*/)) * eventCount;
+ break;
+ }
- // Painter
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- helper.ensureCapacity( lanes.numEvents );
+ xys = ensureCapacityFloat32(xys, 2 * numVertices);
+ rgbas = ensureCapacityFloat32(rgbas, 4 * numVertices);
+ },
+ fillEvent: function (laneIndex: number, eventIndex: number, indexXys: number, indexRgbas: number, viewport: BoundsUnmodifiable): { indexXys: number; indexRgbas: number } {
+ const lane: TimelineLane = lanes.lane(laneIndex);
+ const event: TimelineEventModel = lane.event(eventIndex);
- var indexXys = 0;
- var indexRgbas = 0;
-
- for ( var l = 0; l < lanes.length; l++ ) {
- var lane = lanes.lane( l );
- for ( var e = 0; e < lane.length; e++ ) {
- var event = lane.event( e );
- var indexes = helper.fillEvent( l, e, indexXys, indexRgbas, viewport );
- indexXys = indexes.indexXys;
- indexRgbas = indexes.indexRgbas;
+ const wBorder = borderThickness / viewport.w;
+ const hBorder = borderThickness / viewport.h;
+
+ const _topMargin = hasval(event.topMargin) ? event.topMargin : topMargin;
+ const _bottomMargin = hasval(event.bottomMargin) ? event.bottomMargin : bottomMargin;
+
+ const jTop = rowTopPadding + (laneIndex) * laneHeight + _topMargin;
+ const yTop = (viewport.h - jTop) / viewport.h;
+ const jBottom = rowTopPadding + (laneIndex + 1) * laneHeight - _bottomMargin;
+ const yBottom = (viewport.h - jBottom) / viewport.h;
+
+ const xLeft = timeAxis.tFrac(event.start_PMILLIS);
+ const xRight = timeAxis.tFrac(event.end_PMILLIS);
+
+ const xWidthPixels = viewport.w * (xRight - xLeft);
+
+ if (!(xRight < 0 || xLeft > 1) && xWidthPixels > minimumVisibleWidth) {
+
+ // Fill
+ let fillColor = (event.bgColor || defaultColor);
+ if (event === selection.hoveredEvent.value) {
+ fillColor = darker(fillColor, 0.8);
+ }
+ indexXys = putQuadXys(xys, indexXys, xLeft + wBorder, xRight - wBorder, yTop - hBorder, yBottom + hBorder);
+ indexRgbas = putQuadRgbas(rgbas, indexRgbas, fillColor);
+
+ // Border
+ let borderColor = (event.borderColor || (event.bgColor ? fillColor : null) || defaultBorderColor || fillColor);
+ if (selection.selectedEvents.hasValue(event)) {
+ borderColor = (selectedBorderColor || borderColor);
+ }
+ if (borderColor) {
+ switch (cornerType) {
+ case JointType.BEVEL:
+ // Quads
+ indexXys = putQuadXys(xys, indexXys, xLeft, xLeft + wBorder, yTop - hBorder, yBottom + hBorder);
+ indexXys = putQuadXys(xys, indexXys, xRight - wBorder, xRight, yTop - hBorder, yBottom + hBorder);
+ indexXys = putQuadXys(xys, indexXys, xLeft + wBorder, xRight - wBorder, yTop, yTop - hBorder);
+ indexXys = putQuadXys(xys, indexXys, xLeft + wBorder, xRight - wBorder, yBottom + hBorder, yBottom);
+ indexRgbas = putRgbas(rgbas, indexRgbas, borderColor, 24);
+ // Triangles
+ indexXys = putLowerLeftTriangleXys(xys, indexXys, xRight - wBorder, xRight, yTop, yTop - hBorder);
+ indexXys = putUpperLeftTriangleXys(xys, indexXys, xRight - wBorder, xRight, yBottom + hBorder, yBottom);
+ indexXys = putUpperRightTriangleXys(xys, indexXys, xLeft, xLeft + wBorder, yBottom + hBorder, yBottom);
+ indexXys = putLowerRightTriangleXys(xys, indexXys, xLeft, xLeft + wBorder, yTop, yTop - hBorder);
+ indexRgbas = putRgbas(rgbas, indexRgbas, borderColor, 12);
+ break;
+
+ default:
+ indexXys = putQuadXys(xys, indexXys, xLeft, xRight - wBorder, yTop, yTop - hBorder);
+ indexXys = putQuadXys(xys, indexXys, xRight - wBorder, xRight, yTop, yBottom + hBorder);
+ indexXys = putQuadXys(xys, indexXys, xLeft + wBorder, xRight, yBottom + hBorder, yBottom);
+ indexXys = putQuadXys(xys, indexXys, xLeft, xLeft + wBorder, yTop - hBorder, yBottom);
+ indexRgbas = putRgbas(rgbas, indexRgbas, borderColor, 24);
+ break;
}
}
+ }
- helper.paint( indexXys, indexRgbas, gl, viewport );
- };
+ return { indexXys: indexXys, indexRgbas: indexRgbas };
+ }
+ };
+}
+
+
+export function newEventBarsPainterFactory(barOpts?: TimelineEventBarsPainterOptions): TimelineEventsPainterFactory {
+
+ // Painter Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions): Painter {
+
+ const helper = eventBarPainterHelper(barOpts, drawable, timeAxis, lanes, ui, options);
+
+ // Painter
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ helper.ensureCapacity(lanes.numEvents);
+
+ let indexXys = 0;
+ let indexRgbas = 0;
+
+ for (let l = 0; l < lanes.length; l++) {
+ const lane = lanes.lane(l);
+ for (let e = 0; e < lane.length; e++) {
+ const event = lane.event(e);
+ const indexes = helper.fillEvent(l, e, indexXys, indexRgbas, viewport);
+ indexXys = indexes.indexXys;
+ indexRgbas = indexes.indexRgbas;
+ }
+ }
+
+ helper.paint(indexXys, indexRgbas, gl, viewport);
};
- }
+ };
+}
-
- export function newCombinedEventPainterFactory( barOpts? : TimelineEventBarsPainterOptions, labelOpts? : TimelineEventLabelOptions, iconOpts? : TimelineEventIconsPainterOptions ) : TimelineEventsPainterFactory {
-
- // Painter Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, lanes : TimelineLaneArray, ui : TimelineUi, options : TimelineEventsPainterOptions ) : Painter {
-
- var labelHelper = eventLabelsPainterHelper( labelOpts, drawable, timeAxis, lanes, ui, options );
- var iconHelper = eventIconsPainterHelper( iconOpts, drawable, timeAxis, lanes, ui, options );
- var barHelper = eventStripedBarPainterHelper( barOpts, drawable, timeAxis, lanes, ui, options );
- var dashedHelper = eventDashedBorderPainterHelper( barOpts, drawable, timeAxis, lanes, ui, options );
-
- // Painter
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
-
- for ( var l = 0; l < lanes.length; l++ ) {
- var lane : TimelineLane = lanes.lane( l );
- for ( var e = 0; e < lane.length; e++ ) {
-
- // draw bar
- barHelper.ensureCapacity( 1 );
- var indexes = barHelper.fillEvent( l, e, 0, 0, viewport, 0, 0 );
- var dashedIndexes = dashedHelper.fillEvent( l, e, 0, 0, viewport, 0 );
- barHelper.paint( indexes.indexXys, indexes.indexRgbas, gl, viewport, indexes.indexRelativeXys, indexes.indexFillPattern );
- dashedHelper.paint( dashedIndexes.indexXys, dashedIndexes.indexRgbas, gl, viewport, dashedIndexes.indexLengthSoFar );
-
- // draw label
- labelHelper.textTextures.resetTouches( );
- labelHelper.textureRenderer.begin( gl, viewport );
- labelHelper.paintEvent( l, e, gl, viewport );
- labelHelper.textureRenderer.end( gl );
- labelHelper.textTextures.retainTouched( );
-
- // draw icon
- iconHelper.textureRenderer.begin( gl, viewport );
- iconHelper.paintEvent( l, e, gl, viewport );
- iconHelper.textureRenderer.end( gl );
- }
+
+export function newCombinedEventPainterFactory(barOpts?: TimelineEventBarsPainterOptions, labelOpts?: TimelineEventLabelOptions, iconOpts?: TimelineEventIconsPainterOptions): TimelineEventsPainterFactory {
+
+ // Painter Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, lanes: TimelineLaneArray, ui: TimelineUi, options: TimelineEventsPainterOptions): Painter {
+
+ const labelHelper = eventLabelsPainterHelper(labelOpts, drawable, timeAxis, lanes, ui, options);
+ const iconHelper = eventIconsPainterHelper(iconOpts, drawable, timeAxis, lanes, ui, options);
+ const barHelper = eventStripedBarPainterHelper(barOpts, drawable, timeAxis, lanes, ui, options);
+ const dashedHelper = eventDashedBorderPainterHelper(barOpts, drawable, timeAxis, lanes, ui, options);
+
+ // Painter
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ for (let l = 0; l < lanes.length; l++) {
+ const lane: TimelineLane = lanes.lane(l);
+ for (let e = 0; e < lane.length; e++) {
+
+ // draw bar
+ barHelper.ensureCapacity(1);
+ const indexes = barHelper.fillEvent(l, e, 0, 0, viewport, 0, 0);
+ const dashedIndexes = dashedHelper.fillEvent(l, e, 0, 0, viewport, 0);
+ barHelper.paint(indexes.indexXys, indexes.indexRgbas, gl, viewport, indexes.indexRelativeXys, indexes.indexFillPattern);
+ dashedHelper.paint(dashedIndexes.indexXys, dashedIndexes.indexRgbas, gl, viewport, dashedIndexes.indexLengthSoFar);
+
+ // draw label
+ labelHelper.textTextures.resetTouches();
+ labelHelper.textureRenderer.begin(gl, viewport);
+ labelHelper.paintEvent(l, e, gl, viewport);
+ labelHelper.textureRenderer.end(gl);
+ labelHelper.textTextures.retainTouched();
+
+ // draw icon
+ iconHelper.textureRenderer.begin(gl, viewport);
+ iconHelper.paintEvent(l, e, gl, viewport);
+ iconHelper.textureRenderer.end(gl);
}
-
}
- }
- }
+
+ };
+ };
}
+
diff --git a/src/webglimpse/timeline/timeline_lanes.ts b/src/webglimpse/timeline/timeline_lanes.ts
index 2d172f1..f855141 100644
--- a/src/webglimpse/timeline/timeline_lanes.ts
+++ b/src/webglimpse/timeline/timeline_lanes.ts
@@ -27,542 +27,564 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
-
- export class TimelineLaneArray {
- private _ui : TimelineUi;
- private _row : TimelineRowModel;
- private _lanes : TimelineLane[];
- private _laneNums : StringMap;
-
- private _rebuildLanesMouseWheel;
- private _rebuildLanes;
- private _newEvent;
- private _addEvent;
- private _removeEvent;
-
- private _model : TimelineModel;
-
- // Keep references to listeners, so that we can remove them later
- private _eventAttrsListeners : StringMap;
-
- constructor( model : TimelineModel, row : TimelineRowModel, ui : TimelineUi, allowMultipleLanes : boolean ) {
- this._model = model;
- this._row = row;
- this._ui = ui;
-
- this._lanes = [];
- this._laneNums = {};
- this._eventAttrsListeners = {};
-
- var self = this;
-
- function findAvailableLaneNum( event : TimelineEventModel, startLaneNum : number, endLaneNum : number ) : number {
- for ( var n = startLaneNum; n < endLaneNum; n++ ) {
- if ( self._lanes[ n ].couldFitEvent( event ) ) {
- return n;
- }
+import { TimelineUi } from './timeline_ui';
+import { TimelineRowModel, TimelineModel, TimelineEventModel } from './timeline_model';
+import { StringMap, order, formatTime_ISO8601, hasval } from '../util/util';
+import { Listener } from '../util/notification';
+import { indexAfter, indexBefore, indexAtOrAfter } from '../util/sorted_arrays';
+
+export class TimelineLaneArray {
+ private _ui: TimelineUi;
+ private _row: TimelineRowModel;
+ private _lanes: TimelineLane[];
+ private _laneNums: StringMap;
+
+ private _rebuildLanesMouseWheel: () => void;
+ private _rebuildLanes: () => void;
+ private _newEvent: (eventGuid: string) => void;
+ private _addEvent: (eventGuid: string) => void;
+ private _removeEvent: (eventGuid: string) => void;
+
+ private _model: TimelineModel;
+
+ // Keep references to listeners, so that we can remove them later
+ private _eventAttrsListeners: StringMap;
+
+ constructor(model: TimelineModel, row: TimelineRowModel, ui: TimelineUi, allowMultipleLanes: boolean) {
+ this._model = model;
+ this._row = row;
+ this._ui = ui;
+
+ this._lanes = [];
+ this._laneNums = {};
+ this._eventAttrsListeners = {};
+
+ const self = this;
+
+ function findAvailableLaneNum(event: TimelineEventModel, startLaneNum: number, endLaneNum: number): number {
+ for (let n = startLaneNum; n < endLaneNum; n++) {
+ if (self._lanes[n].couldFitEvent(event)) {
+ return n;
}
- return null;
}
+ return null;
+ }
- function firstAvailableLaneNum( event : TimelineEventModel ) : number {
- var laneNum = findAvailableLaneNum( event, 0, self._lanes.length );
- return ( hasval( laneNum ) ? laneNum : self._lanes.length );
- }
+ function firstAvailableLaneNum(event: TimelineEventModel): number {
+ const laneNum = findAvailableLaneNum(event, 0, self._lanes.length);
+ return (hasval(laneNum) ? laneNum : self._lanes.length);
+ }
- function addEventToLane( event : TimelineEventModel, laneNum : number ) {
- if ( !self._lanes[ laneNum ] ) {
- self._lanes[ laneNum ] = allowMultipleLanes ? new TimelineLaneStack( ui ) : new TimelineLaneSimple( ui );
- }
- self._lanes[ laneNum ].add( event );
- self._laneNums[ event.eventGuid ] = laneNum;
+ function addEventToLane(event: TimelineEventModel, laneNum: number) {
+ if (!self._lanes[laneNum]) {
+ self._lanes[laneNum] = allowMultipleLanes ? new TimelineLaneStack(ui) : new TimelineLaneSimple(ui);
}
-
- function fillVacancy( vacancyLaneNum : number, vacancyEdges_PMILLIS : number[] ) {
- var vacancyLane = self._lanes[ vacancyLaneNum ];
- for ( var n = vacancyLaneNum + 1; n < self._lanes.length; n++ ) {
- var lane = self._lanes[ n ];
- var possibleTenants = lane.collisionsWithInterval( vacancyEdges_PMILLIS[ 0 ], vacancyEdges_PMILLIS[ 1 ] );
- for ( var p = 0; p < possibleTenants.length; p++ ) {
- var event = possibleTenants[ p ];
- if ( vacancyLane.couldFitEvent( event ) ) {
- lane.remove( event );
- addEventToLane( event, vacancyLaneNum );
- fillVacancy( n, effectiveEdges_PMILLIS( ui, event ) );
- }
+ self._lanes[laneNum].add(event);
+ self._laneNums[event.eventGuid] = laneNum;
+ }
+
+ function fillVacancy(vacancyLaneNum: number, vacancyEdges_PMILLIS: number[]) {
+ const vacancyLane = self._lanes[vacancyLaneNum];
+ for (let n = vacancyLaneNum + 1; n < self._lanes.length; n++) {
+ const lane = self._lanes[n];
+ const possibleTenants = lane.collisionsWithInterval(vacancyEdges_PMILLIS[0], vacancyEdges_PMILLIS[1]);
+ for (let p = 0; p < possibleTenants.length; p++) {
+ const event = possibleTenants[p];
+ if (vacancyLane.couldFitEvent(event)) {
+ lane.remove(event);
+ addEventToLane(event, vacancyLaneNum);
+ fillVacancy(n, effectiveEdges_PMILLIS(ui, event));
}
}
}
+ }
- function trimEmptyLanes( ) {
- for ( var n = self._lanes.length - 1; n >= 0; n-- ) {
- if ( self._lanes[ n ].isEmpty( ) ) {
- self._lanes.splice( n, 1 );
- }
- else {
- break;
- }
+ function trimEmptyLanes() {
+ for (let n = self._lanes.length - 1; n >= 0; n--) {
+ if (self._lanes[n].isEmpty()) {
+ self._lanes.splice(n, 1);
}
- }
-
- // adds event to lane, may be called multiple times
- this._addEvent = function( eventGuid : string ) {
- if ( hasval( self._laneNums[ eventGuid ] ) ) {
- throw new Error( 'Lanes-array already contains this event: row-guid = ' + row.rowGuid + ', lane = ' + self._laneNums[ eventGuid ] + ', event-guid = ' + eventGuid );
+ else {
+ break;
}
- var event = model.event( eventGuid );
- var laneNum = firstAvailableLaneNum( event );
- addEventToLane( event, laneNum );
- };
+ }
+ }
- row.eventGuids.forEach( this._addEvent );
- row.eventGuids.valueAdded.on( this._addEvent );
-
- // attaches listeners to event, should be called only once
- // when an event is first added to the row model
- this._newEvent = function( eventGuid : string ) {
- var event = model.event( eventGuid );
- var oldEdges_PMILLIS = effectiveEdges_PMILLIS( ui, event );
- var updateLaneAssignment = function( ) {
- var newEdges_PMILLIS = effectiveEdges_PMILLIS( ui, event );
- if ( newEdges_PMILLIS[ 0 ] !== oldEdges_PMILLIS[ 0 ] || newEdges_PMILLIS[ 1 ] !== oldEdges_PMILLIS[ 1 ] ) {
- var oldLaneNum = self._laneNums[ event.eventGuid ];
- var oldLane = self._lanes[ oldLaneNum ];
-
- var betterLaneNum = findAvailableLaneNum( event, 0, oldLaneNum );
- if ( hasval( betterLaneNum ) ) {
- // Move to a better lane
- oldLane.remove( event );
- addEventToLane( event, betterLaneNum );
- }
- else if ( oldLane.eventStillFits( event ) ) {
- // Stay in the current lane
- oldLane.update( event );
- }
- else {
- // Take whatever lane we can get
- var newLaneNum = findAvailableLaneNum( event, oldLaneNum + 1, self._lanes.length );
- if ( !hasval( newLaneNum ) ) newLaneNum = self._lanes.length;
- oldLane.remove( event );
- addEventToLane( event, newLaneNum );
+ // adds event to lane, may be called multiple times
+ this._addEvent = function (eventGuid: string) {
+ if (hasval(self._laneNums[eventGuid])) {
+ throw new Error('Lanes-array already contains this event: row-guid = ' + row.rowGuid + ', lane = ' + self._laneNums[eventGuid] + ', event-guid = ' + eventGuid);
+ }
+ const event = model.event(eventGuid);
+ const laneNum = firstAvailableLaneNum(event);
+ addEventToLane(event, laneNum);
+ };
+
+ row.eventGuids.forEach(this._addEvent);
+ row.eventGuids.valueAdded.on(this._addEvent);
+
+ // attaches listeners to event, should be called only once
+ // when an event is first added to the row model
+ this._newEvent = function (eventGuid: string) {
+ const event = model.event(eventGuid);
+ let oldEdges_PMILLIS = effectiveEdges_PMILLIS(ui, event);
+ const updateLaneAssignment = function () {
+ const newEdges_PMILLIS = effectiveEdges_PMILLIS(ui, event);
+ if (newEdges_PMILLIS[0] !== oldEdges_PMILLIS[0] || newEdges_PMILLIS[1] !== oldEdges_PMILLIS[1]) {
+ const oldLaneNum = self._laneNums[event.eventGuid];
+ const oldLane = self._lanes[oldLaneNum];
+
+ const betterLaneNum = findAvailableLaneNum(event, 0, oldLaneNum);
+ if (hasval(betterLaneNum)) {
+ // Move to a better lane
+ oldLane.remove(event);
+ addEventToLane(event, betterLaneNum);
+ }
+ else if (oldLane.eventStillFits(event)) {
+ // Stay in the current lane
+ oldLane.update(event);
+ }
+ else {
+ // Take whatever lane we can get
+ let newLaneNum = findAvailableLaneNum(event, oldLaneNum + 1, self._lanes.length);
+ if (!hasval(newLaneNum)){
+ newLaneNum = self._lanes.length;
}
-
- fillVacancy( oldLaneNum, oldEdges_PMILLIS );
- trimEmptyLanes( );
-
- oldEdges_PMILLIS = newEdges_PMILLIS;
+ oldLane.remove(event);
+ addEventToLane(event, newLaneNum);
}
- };
-
- event.attrsChanged.on( updateLaneAssignment );
- self._eventAttrsListeners[ eventGuid ] = updateLaneAssignment;
- };
- row.eventGuids.forEach( this._newEvent );
- row.eventGuids.valueAdded.on( this._newEvent );
- this._removeEvent = function( eventGuid : string ) {
- var event = model.event( eventGuid );
+ fillVacancy(oldLaneNum, oldEdges_PMILLIS);
+ trimEmptyLanes();
- var oldLaneNum = self._laneNums[ eventGuid ];
- delete self._laneNums[ eventGuid ];
-
- self._lanes[ oldLaneNum ].remove( event );
- fillVacancy( oldLaneNum, effectiveEdges_PMILLIS( ui, event ) );
- trimEmptyLanes( );
-
- event.attrsChanged.off( self._eventAttrsListeners[ eventGuid ] );
- delete self._eventAttrsListeners[ eventGuid ];
- }
- row.eventGuids.valueRemoved.on( this._removeEvent );
-
- self._rebuildLanes = function( ) {
- var oldLanes = self._lanes;
- self._lanes = [];
- self._laneNums = {};
-
- for ( var l = 0; l < oldLanes.length; l++ ) {
- var lane = oldLanes[ l ];
- for ( var e = 0; e < lane.length; e++ ) {
- var event = lane.event( e );
- self._addEvent( event.eventGuid );
- }
+ oldEdges_PMILLIS = newEdges_PMILLIS;
}
};
-
- var hasIcons = function( ) {
- var oldLanes = self._lanes;
- for ( var l = 0; l < oldLanes.length; l++ ) {
- var lane = oldLanes[ l ];
- for ( var e = 0; e < lane.length; e++ ) {
- var event = lane.event( e );
- var style = ui.eventStyle( event.styleGuid );
- if ( event.labelIcon || style.numIcons > 0 ) return true;
- }
+
+ event.attrsChanged.on(updateLaneAssignment);
+ self._eventAttrsListeners[eventGuid] = updateLaneAssignment;
+ };
+ row.eventGuids.forEach(this._newEvent);
+ row.eventGuids.valueAdded.on(this._newEvent);
+
+ this._removeEvent = function (eventGuid: string) {
+ const event = model.event(eventGuid);
+
+ const oldLaneNum = self._laneNums[eventGuid];
+ delete self._laneNums[eventGuid];
+
+ self._lanes[oldLaneNum].remove(event);
+ fillVacancy(oldLaneNum, effectiveEdges_PMILLIS(ui, event));
+ trimEmptyLanes();
+
+ event.attrsChanged.off(self._eventAttrsListeners[eventGuid]);
+ delete self._eventAttrsListeners[eventGuid];
+ };
+ row.eventGuids.valueRemoved.on(this._removeEvent);
+
+ self._rebuildLanes = function () {
+ const oldLanes = self._lanes;
+ self._lanes = [];
+ self._laneNums = {};
+
+ for (let l = 0; l < oldLanes.length; l++) {
+ const lane = oldLanes[l];
+ for (let e = 0; e < lane.length; e++) {
+ const event = lane.event(e);
+ self._addEvent(event.eventGuid);
}
- return false;
}
-
- self._rebuildLanesMouseWheel = function( ) {
- if ( hasIcons( ) ) {
- self._rebuildLanes( );
+ };
+
+ const hasIcons = function () {
+ const oldLanes = self._lanes;
+ for (let l = 0; l < oldLanes.length; l++) {
+ const lane = oldLanes[l];
+ for (let e = 0; e < lane.length; e++) {
+ const event = lane.event(e);
+ const style = ui.eventStyle(event.styleGuid);
+ if (event.labelIcon || style.numIcons > 0) {
+ return true;
+ }
}
}
-
- ui.millisPerPx.changed.on( self._rebuildLanesMouseWheel );
- ui.eventStyles.valueAdded.on( self._rebuildLanes );
- ui.eventStyles.valueRemoved.on( self._rebuildLanes );
- }
+ return false;
+ };
- get length( ) : number {
- return this._lanes.length;
- }
+ self._rebuildLanesMouseWheel = function () {
+ if (hasIcons()) {
+ self._rebuildLanes();
+ }
+ };
- lane( index : number ) : TimelineLane {
- return this._lanes[ index ];
- }
+ ui.millisPerPx.changed.on(self._rebuildLanesMouseWheel);
+ ui.eventStyles.valueAdded.on(self._rebuildLanes);
+ ui.eventStyles.valueRemoved.on(self._rebuildLanes);
+ }
- get numEvents( ) : number {
- return this._row.eventGuids.length;
- }
+ get length(): number {
+ return this._lanes.length;
+ }
- eventAt( laneNum : number, time_PMILLIS : number ) : TimelineEventModel {
- var lane = this._lanes[ laneNum ];
- return ( lane && lane.eventAtTime( time_PMILLIS ) );
- }
-
- dispose( ) : void {
- this._row.eventGuids.valueAdded.off( this._addEvent );
- this._row.eventGuids.valueRemoved.off( this._removeEvent );
- this._row.eventGuids.valueAdded.off( this._newEvent );
-
- this._ui.millisPerPx.changed.off( this._rebuildLanesMouseWheel );
- this._ui.eventStyles.valueAdded.off( this._rebuildLanes );
- this._ui.eventStyles.valueRemoved.off( this._rebuildLanes );
-
- for ( var eventGuid in this._eventAttrsListeners ) {
- if ( this._eventAttrsListeners.hasOwnProperty( eventGuid ) ) {
- var listener = this._eventAttrsListeners[ eventGuid ];
- var event = this._model.event( eventGuid );
- if ( listener && event ) event.attrsChanged.off( listener );
+ lane(index: number): TimelineLane {
+ return this._lanes[index];
+ }
+
+ get numEvents(): number {
+ return this._row.eventGuids.length;
+ }
+
+ eventAt(laneNum: number, time_PMILLIS: number): TimelineEventModel {
+ const lane = this._lanes[laneNum];
+ return (lane && lane.eventAtTime(time_PMILLIS));
+ }
+
+ dispose(): void {
+ this._row.eventGuids.valueAdded.off(this._addEvent);
+ this._row.eventGuids.valueRemoved.off(this._removeEvent);
+ this._row.eventGuids.valueAdded.off(this._newEvent);
+
+ this._ui.millisPerPx.changed.off(this._rebuildLanesMouseWheel);
+ this._ui.eventStyles.valueAdded.off(this._rebuildLanes);
+ this._ui.eventStyles.valueRemoved.off(this._rebuildLanes);
+
+ for (const eventGuid in this._eventAttrsListeners) {
+ if (this._eventAttrsListeners.hasOwnProperty(eventGuid)) {
+ const listener = this._eventAttrsListeners[eventGuid];
+ const event = this._model.event(eventGuid);
+ if (listener && event) {
+ event.attrsChanged.off(listener);
}
}
}
}
+}
- export function effectiveEdges_PMILLIS( ui : TimelineUi, event : TimelineEventModel ) : number[] {
- var start_PMILLIS = event.start_PMILLIS;
- var end_PMILLIS = event.end_PMILLIS;
+export function effectiveEdges_PMILLIS(ui: TimelineUi, event: TimelineEventModel): number[] {
+ let start_PMILLIS = event.start_PMILLIS;
+ let end_PMILLIS = event.end_PMILLIS;
- var millisPerPx = ui.millisPerPx.value;
- var eventStyle = ui.eventStyle( event.styleGuid );
- for ( var n = 0; n < eventStyle.numIcons; n++ ) {
- var icon = eventStyle.icon( n );
- var iconTime_PMILLIS = event.start_PMILLIS + icon.hPos*( event.end_PMILLIS - event.start_PMILLIS );
- var iconStart_PMILLIS = iconTime_PMILLIS - ( millisPerPx * icon.hAlign * icon.displayWidth );
- var iconEnd_PMILLIS = iconTime_PMILLIS + ( millisPerPx * (1-icon.hAlign) * icon.displayWidth );
+ const millisPerPx = ui.millisPerPx.value;
+ const eventStyle = ui.eventStyle(event.styleGuid);
+ for (let n = 0; n < eventStyle.numIcons; n++) {
+ const icon = eventStyle.icon(n);
+ const iconTime_PMILLIS = event.start_PMILLIS + icon.hPos * (event.end_PMILLIS - event.start_PMILLIS);
+ const iconStart_PMILLIS = iconTime_PMILLIS - (millisPerPx * icon.hAlign * icon.displayWidth);
+ const iconEnd_PMILLIS = iconTime_PMILLIS + (millisPerPx * (1 - icon.hAlign) * icon.displayWidth);
- start_PMILLIS = Math.min( start_PMILLIS, iconStart_PMILLIS );
- end_PMILLIS = Math.max( end_PMILLIS, iconEnd_PMILLIS );
- }
+ start_PMILLIS = Math.trunc(Math.min(start_PMILLIS, iconStart_PMILLIS));
+ end_PMILLIS = Math.trunc(Math.max(end_PMILLIS, iconEnd_PMILLIS));
+ }
- return [ start_PMILLIS, end_PMILLIS ];
- }
-
-
- export interface TimelineLane {
- length : number;
- event( index : number ) : TimelineEventModel;
- isEmpty( ) : boolean;
- eventAtTime( time_PMILLIS : number ) : TimelineEventModel;
- add( event : TimelineEventModel );
- remove( event : TimelineEventModel );
- eventStillFits( event : TimelineEventModel ) : boolean;
- update( event : TimelineEventModel );
- collisionsWithInterval( start_PMILLIS : number, end_PMILLIS : number ) : TimelineEventModel[];
- couldFitEvent( event : TimelineEventModel ) : boolean;
- }
-
- // a TimelineLane where no events start/end time overlap
- export class TimelineLaneStack implements TimelineLane {
- private _events : TimelineEventModel[];
- private _starts_PMILLIS : number[];
- private _ends_PMILLIS : number[];
- private _indices : StringMap;
- private _ui : TimelineUi;
-
- constructor( ui : TimelineUi ) {
- this._events = [];
- this._starts_PMILLIS = [];
- this._ends_PMILLIS = [];
- this._indices = {};
- this._ui = ui;
- }
+ return [start_PMILLIS, end_PMILLIS];
+}
+
+
+export interface TimelineLane {
+ length: number;
+ event(index: number): TimelineEventModel;
+ isEmpty(): boolean;
+ eventAtTime(time_PMILLIS: number): TimelineEventModel;
+ add(event: TimelineEventModel): void;
+ remove(event: TimelineEventModel): void;
+ eventStillFits(event: TimelineEventModel): boolean;
+ update(event: TimelineEventModel): void;
+ collisionsWithInterval(start_PMILLIS: number, end_PMILLIS: number): TimelineEventModel[];
+ couldFitEvent(event: TimelineEventModel): boolean;
+}
+
+// a TimelineLane where no events start/end time overlap
+export class TimelineLaneStack implements TimelineLane {
+ private _events: TimelineEventModel[];
+ private _starts_PMILLIS: number[];
+ private _ends_PMILLIS: number[];
+ private _indices: StringMap;
+ private _ui: TimelineUi;
+
+ constructor(ui: TimelineUi) {
+ this._events = [];
+ this._starts_PMILLIS = [];
+ this._ends_PMILLIS = [];
+ this._indices = {};
+ this._ui = ui;
+ }
- get length( ) : number {
- return this._events.length;
- }
+ get length(): number {
+ return this._events.length;
+ }
- event( index : number ) : TimelineEventModel {
- return this._events[ index ];
- }
+ event(index: number): TimelineEventModel {
+ return this._events[index];
+ }
- isEmpty( ) : boolean {
- return ( this._events.length === 0 );
- }
+ isEmpty(): boolean {
+ return (this._events.length === 0);
+ }
- eventAtTime( time_PMILLIS : number ) : TimelineEventModel {
- if ( hasval( time_PMILLIS ) ) {
- // Check the first event ending after time
- var iFirst = indexAfter( this._ends_PMILLIS, time_PMILLIS );
- if ( iFirst < this._events.length ) {
- var eventFirst = this._events[ iFirst ];
- var startFirst_PMILLIS = effectiveEdges_PMILLIS( this._ui, eventFirst )[ 0 ];
- if ( time_PMILLIS >= startFirst_PMILLIS ) {
- return eventFirst;
- }
+ eventAtTime(time_PMILLIS: number): TimelineEventModel {
+ if (hasval(time_PMILLIS)) {
+ // Check the first event ending after time
+ const iFirst = indexAfter(this._ends_PMILLIS, time_PMILLIS);
+ if (iFirst < this._events.length) {
+ const eventFirst = this._events[iFirst];
+ const startFirst_PMILLIS = effectiveEdges_PMILLIS(this._ui, eventFirst)[0];
+ if (time_PMILLIS >= startFirst_PMILLIS) {
+ return eventFirst;
}
- // Check the previous event, in case we're in its icon-slop
- var iPrev = iFirst - 1;
- if ( iPrev >= 0 ) {
- var eventPrev = this._events[ iPrev ];
- var endPrev_PMILLIS = effectiveEdges_PMILLIS( this._ui, eventPrev )[ 1 ];
- if ( time_PMILLIS < endPrev_PMILLIS ) {
- return eventPrev;
- }
+ }
+ // Check the previous event, in case we're in its icon-slop
+ const iPrev = iFirst - 1;
+ if (iPrev >= 0) {
+ const eventPrev = this._events[iPrev];
+ const endPrev_PMILLIS = effectiveEdges_PMILLIS(this._ui, eventPrev)[1];
+ if (time_PMILLIS < endPrev_PMILLIS) {
+ return eventPrev;
}
}
- return null;
}
+ return null;
+ }
- add( event : TimelineEventModel ) {
- var eventGuid = event.eventGuid;
- if ( hasval( this._indices[ eventGuid ] ) ) throw new Error( 'Lane already contains this event: event = ' + formatEvent( event ) );
+ add(event: TimelineEventModel) {
+ const eventGuid = event.eventGuid;
+ if (hasval(this._indices[eventGuid])) {
+ throw new Error('Lane already contains this event: event = ' + formatEvent(event));
+ }
- var i = indexAfter( this._starts_PMILLIS, event.start_PMILLIS );
- if ( !this._eventFitsBetween( event, i-1, i ) ) throw new Error( 'New event does not fit between existing events: new = ' + formatEvent( event ) + ', before = ' + formatEvent( this._events[ i-1 ] ) + ', after = ' +formatEvent( this._events[ i ] ) );
+ const i = indexAfter(this._starts_PMILLIS, event.start_PMILLIS);
+ if (!this._eventFitsBetween(event, i - 1, i)) {
+ throw new Error('New event does not fit between existing events: new = ' + formatEvent(event) + ', before = ' + formatEvent(this._events[i - 1]) + ', after = ' + formatEvent(this._events[i]));
+ }
- this._events.splice( i, 0, event );
- this._starts_PMILLIS.splice( i, 0, event.start_PMILLIS );
- this._ends_PMILLIS.splice( i, 0, event.end_PMILLIS );
- this._indices[ eventGuid ] = i;
+ this._events.splice(i, 0, event);
+ this._starts_PMILLIS.splice(i, 0, event.start_PMILLIS);
+ this._ends_PMILLIS.splice(i, 0, event.end_PMILLIS);
+ this._indices[eventGuid] = i;
- for ( var n = i; n < this._events.length; n++ ) {
- this._indices[ this._events[ n ].eventGuid ] = n;
- }
+ for (let n = i; n < this._events.length; n++) {
+ this._indices[this._events[n].eventGuid] = n;
}
+ }
- remove( event : TimelineEventModel ) {
- var eventGuid = event.eventGuid;
- var i = this._indices[ eventGuid ];
- if ( !hasval( i ) ) throw new Error( 'Event not found in this lane: event = ' + formatEvent( event ) );
+ remove(event: TimelineEventModel) {
+ const eventGuid = event.eventGuid;
+ const i = this._indices[eventGuid];
+ if (!hasval(i)) {
+ throw new Error('Event not found in this lane: event = ' + formatEvent(event));
+ }
- this._events.splice( i, 1 );
- this._starts_PMILLIS.splice( i, 1 );
- this._ends_PMILLIS.splice( i, 1 );
- delete this._indices[ eventGuid ];
+ this._events.splice(i, 1);
+ this._starts_PMILLIS.splice(i, 1);
+ this._ends_PMILLIS.splice(i, 1);
+ delete this._indices[eventGuid];
- for ( var n = i; n < this._events.length; n++ ) {
- this._indices[ this._events[ n ].eventGuid ] = n;
- }
+ for (let n = i; n < this._events.length; n++) {
+ this._indices[this._events[n].eventGuid] = n;
}
+ }
- eventStillFits( event : TimelineEventModel ) : boolean {
- var i = this._indices[ event.eventGuid ];
- if ( !hasval( i ) ) throw new Error( 'Event not found in this lane: event = ' + formatEvent( event ) );
-
- return this._eventFitsBetween( event, i-1, i+1 );
+ eventStillFits(event: TimelineEventModel): boolean {
+ const i = this._indices[event.eventGuid];
+ if (!hasval(i)) {
+ throw new Error('Event not found in this lane: event = ' + formatEvent(event));
}
- update( event : TimelineEventModel ) {
- var i = this._indices[ event.eventGuid ];
- if ( !hasval( i ) ) throw new Error( 'Event not found in this lane: event = ' + formatEvent( event ) );
+ return this._eventFitsBetween(event, i - 1, i + 1);
+ }
- this._starts_PMILLIS[ i ] = event.start_PMILLIS;
- this._ends_PMILLIS[ i ] = event.end_PMILLIS;
+ update(event: TimelineEventModel) {
+ const i = this._indices[event.eventGuid];
+ if (!hasval(i)) {
+ throw new Error('Event not found in this lane: event = ' + formatEvent(event));
}
- collisionsWithInterval( start_PMILLIS : number, end_PMILLIS : number ) : TimelineEventModel[] {
- // Find the first event ending after start
- var iFirst = indexAfter( this._ends_PMILLIS, start_PMILLIS );
- var iPrev = iFirst - 1;
- if ( iPrev >= 0 ) {
- var endPrev_PMILLIS = effectiveEdges_PMILLIS( this._ui, this._events[ iPrev ] )[ 1 ];
- if ( start_PMILLIS < endPrev_PMILLIS ) {
- iFirst = iPrev;
- }
+ this._starts_PMILLIS[i] = event.start_PMILLIS;
+ this._ends_PMILLIS[i] = event.end_PMILLIS;
+ }
+
+ collisionsWithInterval(start_PMILLIS: number, end_PMILLIS: number): TimelineEventModel[] {
+ // Find the first event ending after start
+ let iFirst = indexAfter(this._ends_PMILLIS, start_PMILLIS);
+ const iPrev = iFirst - 1;
+ if (iPrev >= 0) {
+ const endPrev_PMILLIS = effectiveEdges_PMILLIS(this._ui, this._events[iPrev])[1];
+ if (start_PMILLIS < endPrev_PMILLIS) {
+ iFirst = iPrev;
}
- // Find the last event starting before end
- var iLast = indexBefore( this._starts_PMILLIS, end_PMILLIS );
- var iPost = iLast + 1;
- if ( iPost < this._events.length ) {
- var startPost_PMILLIS = effectiveEdges_PMILLIS( this._ui, this._events[ iPost ] )[ 0 ];
- if ( end_PMILLIS > startPost_PMILLIS ) {
- iLast = iPost;
- }
+ }
+ // Find the last event starting before end
+ let iLast = indexBefore(this._starts_PMILLIS, end_PMILLIS);
+ const iPost = iLast + 1;
+ if (iPost < this._events.length) {
+ const startPost_PMILLIS = effectiveEdges_PMILLIS(this._ui, this._events[iPost])[0];
+ if (end_PMILLIS > startPost_PMILLIS) {
+ iLast = iPost;
}
- // Return that section
- return this._events.slice( iFirst, iLast + 1 );
}
+ // Return that section
+ return this._events.slice(iFirst, iLast + 1);
+ }
- couldFitEvent( event : TimelineEventModel ) : boolean {
- var iAfter = indexAfter( this._starts_PMILLIS, event.start_PMILLIS );
- var iBefore = iAfter - 1;
- return this._eventFitsBetween( event, iBefore, iAfter );
- }
+ couldFitEvent(event: TimelineEventModel): boolean {
+ const iAfter = indexAfter(this._starts_PMILLIS, event.start_PMILLIS);
+ const iBefore = iAfter - 1;
+ return this._eventFitsBetween(event, iBefore, iAfter);
+ }
- _eventFitsBetween( event : TimelineEventModel, iBefore : number, iAfter : number ) : boolean {
- var edges_PMILLIS = effectiveEdges_PMILLIS( this._ui, event );
+ _eventFitsBetween(event: TimelineEventModel, iBefore: number, iAfter: number): boolean {
+ const edges_PMILLIS = effectiveEdges_PMILLIS(this._ui, event);
- if ( iBefore >= 0 ) {
- // Comparing one start-time (inclusive) and one end-time (exclusive), so equality means no collision
- var edgesBefore_PMILLIS = effectiveEdges_PMILLIS( this._ui, this._events[ iBefore ] );
- if ( edges_PMILLIS[ 0 ] < edgesBefore_PMILLIS[ 1 ] ) {
- return false;
- }
+ if (iBefore >= 0) {
+ // Comparing one start-time (inclusive) and one end-time (exclusive), so equality means no collision
+ const edgesBefore_PMILLIS = effectiveEdges_PMILLIS(this._ui, this._events[iBefore]);
+ if (edges_PMILLIS[0] < edgesBefore_PMILLIS[1]) {
+ return false;
}
+ }
- if ( iAfter < this._events.length ) {
- // Comparing one start-time (inclusive) and one end-time (exclusive), so equality means no collision
- var edgesAfter_PMILLIS = effectiveEdges_PMILLIS( this._ui, this._events[ iAfter ] );
- if ( edges_PMILLIS[ 1 ] > edgesAfter_PMILLIS[ 0 ] ) {
- return false;
- }
+ if (iAfter < this._events.length) {
+ // Comparing one start-time (inclusive) and one end-time (exclusive), so equality means no collision
+ const edgesAfter_PMILLIS = effectiveEdges_PMILLIS(this._ui, this._events[iAfter]);
+ if (edges_PMILLIS[1] > edgesAfter_PMILLIS[0]) {
+ return false;
}
-
- return true;
}
+
+ return true;
}
-
- // a TimelineLane where events are allowed to overlap arbitrarily
- // because of this assumptions like the index for an event in the _starts_PMILLIS
- // and _ends_PMILLIS arrays being the same no longer hold
- //
- // does not make any assumptions about event overlapping and uses
- // an inefficient O(n) brute force search to find events (an interval tree
- // would be needed for efficient search in the general case)
- export class TimelineLaneSimple implements TimelineLane {
-
- private _events : TimelineEventModel[];
- private _orders : number[];
- private _ids : StringMap;
- private _ui : TimelineUi;
-
- constructor( ui : TimelineUi ) {
- this._events = [];
- this._orders = [];
- this._ids = {};
- this._ui = ui;
- }
-
- get length( ) : number {
- return this._events.length;
- }
-
- event( index : number ) : TimelineEventModel {
- return this._events[ index ];
- }
-
- isEmpty( ) : boolean {
- return ( this._events.length === 0 );
- }
-
- eventAtTime( time_PMILLIS : number ) : TimelineEventModel {
-
- var bestEvent : TimelineEventModel;
-
- // start at end of events list so that eventAtTime result
- // favors events drawn on top (in cases where events are unordered
- // those that happen to be at end end of the list are drawn last
- for ( var n = this._events.length-1; n >= 0; n-- ) {
- var event : TimelineEventModel = this._events[n];
-
- var eventEdges_PMILLIS = effectiveEdges_PMILLIS( this._ui, event );
-
- if ( time_PMILLIS > eventEdges_PMILLIS[0] &&
- time_PMILLIS < eventEdges_PMILLIS[1] &&
- ( bestEvent === undefined || bestEvent.order < event.order ) ) {
- bestEvent = event;
- }
+}
+
+// a TimelineLane where events are allowed to overlap arbitrarily
+// because of this assumptions like the index for an event in the _starts_PMILLIS
+// and _ends_PMILLIS arrays being the same no longer hold
+//
+// does not make any assumptions about event overlapping and uses
+// an inefficient O(n) brute force search to find events (an interval tree
+// would be needed for efficient search in the general case)
+export class TimelineLaneSimple implements TimelineLane {
+
+ private _events: TimelineEventModel[];
+ private _orders: number[];
+ private _ids: StringMap;
+ private _ui: TimelineUi;
+
+ constructor(ui: TimelineUi) {
+ this._events = [];
+ this._orders = [];
+ this._ids = {};
+ this._ui = ui;
+ }
+
+ get length(): number {
+ return this._events.length;
+ }
+
+ event(index: number): TimelineEventModel {
+ return this._events[index];
+ }
+
+ isEmpty(): boolean {
+ return (this._events.length === 0);
+ }
+
+ eventAtTime(time_PMILLIS: number): TimelineEventModel {
+
+ let bestEvent: TimelineEventModel;
+
+ // start at end of events list so that eventAtTime result
+ // favors events drawn on top (in cases where events are unordered
+ // those that happen to be at end end of the list are drawn last
+ for (let n = this._events.length - 1; n >= 0; n--) {
+ const event: TimelineEventModel = this._events[n];
+
+ const eventEdges_PMILLIS = effectiveEdges_PMILLIS(this._ui, event);
+
+ if (time_PMILLIS > eventEdges_PMILLIS[0] &&
+ time_PMILLIS < eventEdges_PMILLIS[1] &&
+ (bestEvent === undefined || bestEvent.order < event.order)) {
+ bestEvent = event;
}
-
- return bestEvent;
- }
-
- add( event : TimelineEventModel ) {
- var eventGuid = event.eventGuid;
- if ( hasval( this._ids[ eventGuid ] ) ) throw new Error( 'Lane already contains this event: event = ' + formatEvent( event ) );
-
- // for events with undefined order, replace with largest possible negative order so sort is correct
- var order = hasval( event.order ) ? event.order : Number.NEGATIVE_INFINITY;
-
- var i : number = indexAtOrAfter( this._orders, order );
-
- this._ids[ eventGuid ] = eventGuid;
- this._orders.splice( i, 0, order );
- this._events.splice( i, 0, event );
}
-
- remove( event : TimelineEventModel ) {
- var eventGuid = event.eventGuid;
- if ( !hasval( this._ids[ eventGuid ] ) ) throw new Error( 'Event not found in this lane: event = ' + formatEvent( event ) );
-
- delete this._ids[ eventGuid ];
- var i : number = this._getIndex( event );
- this._orders.splice( i, 1 );
- this._events.splice( i, 1 );
+
+ return bestEvent;
+ }
+
+ add(event: TimelineEventModel) {
+ const eventGuid = event.eventGuid;
+ if (hasval(this._ids[eventGuid])) {
+ throw new Error('Lane already contains this event: event = ' + formatEvent(event));
}
-
- update( event : TimelineEventModel ) {
- this.remove( event );
- this.add( event );
+
+ // for events with undefined order, replace with largest possible negative order so sort is correct
+ const orderVal = hasval(event.order) ? event.order : Number.NEGATIVE_INFINITY;
+
+ const i: number = indexAtOrAfter(this._orders, orderVal);
+
+ this._ids[eventGuid] = eventGuid;
+ this._orders.splice(i, 0, orderVal);
+ this._events.splice(i, 0, event);
+ }
+
+ remove(event: TimelineEventModel) {
+ const eventGuid = event.eventGuid;
+ if (!hasval(this._ids[eventGuid])) {
+ throw new Error('Event not found in this lane: event = ' + formatEvent(event));
}
-
- collisionsWithInterval( start_PMILLIS : number, end_PMILLIS : number ) : TimelineEventModel[] {
-
- var results = [];
-
- for ( var n = 0; n < this._events.length; n++ ) {
- var event : TimelineEventModel = this._events[n];
-
- if ( !(start_PMILLIS > event.end_PMILLIS || end_PMILLIS < event.start_PMILLIS) ) {
- results.push( event );
- }
+
+ delete this._ids[eventGuid];
+ const i: number = this._getIndex(event);
+ this._orders.splice(i, 1);
+ this._events.splice(i, 1);
+ }
+
+ update(event: TimelineEventModel) {
+ this.remove(event);
+ this.add(event);
+ }
+
+ collisionsWithInterval(start_PMILLIS: number, end_PMILLIS: number): TimelineEventModel[] {
+
+ const results = [];
+
+ for (let n = 0; n < this._events.length; n++) {
+ const event: TimelineEventModel = this._events[n];
+
+ if (!(start_PMILLIS > event.end_PMILLIS || end_PMILLIS < event.start_PMILLIS)) {
+ results.push(event);
}
-
- return results;
- }
-
- // we can always fit more events because overlaps are allowed
- eventStillFits( event : TimelineEventModel ) : boolean {
- return true;
- }
-
- // we can always fit more events because overlaps are allowed
- couldFitEvent( event : TimelineEventModel ) : boolean {
- return true;
}
-
- _getIndex( queryEvent : TimelineEventModel ) : number {
- for ( var n = 0; n < this._events.length; n++ ) {
- var event : TimelineEventModel = this._events[n];
- if ( queryEvent.eventGuid === event.eventGuid ) {
- return n;
- }
+
+ return results;
+ }
+
+ // we can always fit more events because overlaps are allowed
+ eventStillFits(event: TimelineEventModel): boolean {
+ return true;
+ }
+
+ // we can always fit more events because overlaps are allowed
+ couldFitEvent(event: TimelineEventModel): boolean {
+ return true;
+ }
+
+ _getIndex(queryEvent: TimelineEventModel): number {
+ for (let n = 0; n < this._events.length; n++) {
+ const event: TimelineEventModel = this._events[n];
+ if (queryEvent.eventGuid === event.eventGuid) {
+ return n;
}
- throw new Error( 'Event not found in this lane: event = ' + formatEvent( queryEvent ) );
}
+ throw new Error('Event not found in this lane: event = ' + formatEvent(queryEvent));
}
+}
- function formatEvent( event : TimelineEventModel ) : string {
- if ( !hasval( event ) ) {
- return '' + event;
- }
- else {
- return ( event.label + ' [ ' + formatTime_ISO8601( event.start_PMILLIS ) + ' ... ' + formatTime_ISO8601( event.end_PMILLIS ) + ' ]' );
- }
+function formatEvent(event: TimelineEventModel): string {
+ if (!hasval(event)) {
+ return '' + event;
+ }
+ else {
+ return (event.label + ' [ ' + formatTime_ISO8601(event.start_PMILLIS) + ' ... ' + formatTime_ISO8601(event.end_PMILLIS) + ' ]');
}
-}
\ No newline at end of file
+}
diff --git a/src/webglimpse/timeline/timeline_layout.ts b/src/webglimpse/timeline/timeline_layout.ts
index 0466fd7..1ba3cc9 100644
--- a/src/webglimpse/timeline/timeline_layout.ts
+++ b/src/webglimpse/timeline/timeline_layout.ts
@@ -27,102 +27,115 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
-
- export function newTimelineLayout( axisHeight : number ) : Layout {
-
- return {
-
- updatePrefSize: function( parentPrefSize : Size, children : LayoutEntry[] ) {
- var topAxis : LayoutEntry = null;
- var bottomAxis : LayoutEntry = null;
- var center : LayoutEntry = null;
- for ( var c = 0; c < children.length; c++ ) {
- var child = children[ c ];
- switch ( child.layoutArg ) {
- case Side.TOP:
- if ( hasval( topAxis ) ) throw new Error( 'Timeline-layout can have at most one top-axis pane' );
- topAxis = child;
- break;
-
- case Side.BOTTOM:
- if ( hasval( bottomAxis ) ) throw new Error( 'Timeline-layout can have at most one bottom-axis pane' );
- bottomAxis = child;
- break;
-
- default:
- if ( hasval( center ) ) throw new Error( 'Timeline-layout can have at most one center pane' );
- center = child;
- break;
- }
+import { Layout, LayoutEntry } from '../core';
+import { Size, BoundsUnmodifiable } from '../bounds';
+import { Side } from '../misc';
+import { hasval } from '../util/util';
+
+export function newTimelineLayout(axisHeight: number): Layout {
+
+ return {
+
+ updatePrefSize: function (parentPrefSize: Size, children: LayoutEntry[]) {
+ let topAxis: LayoutEntry = null;
+ let bottomAxis: LayoutEntry = null;
+ let center: LayoutEntry = null;
+ for (let c = 0; c < children.length; c++) {
+ const child = children[c];
+ switch (child.layoutArg) {
+ case Side.TOP:
+ if (hasval(topAxis)) {
+ throw new Error('Timeline-layout can have at most one top-axis pane');
+ }
+ topAxis = child;
+ break;
+
+ case Side.BOTTOM:
+ if (hasval(bottomAxis)) {
+ throw new Error('Timeline-layout can have at most one bottom-axis pane');
+ }
+ bottomAxis = child;
+ break;
+
+ default:
+ if (hasval(center)) {
+ throw new Error('Timeline-layout can have at most one center pane');
+ }
+ center = child;
+ break;
}
+ }
- var hSum = 0;
+ let hSum = 0;
- if ( hasval( topAxis ) ) {
- hSum += axisHeight;
- }
+ if (hasval(topAxis)) {
+ hSum += axisHeight;
+ }
- if ( hasval( bottomAxis ) ) {
- hSum += axisHeight;
- }
+ if (hasval(bottomAxis)) {
+ hSum += axisHeight;
+ }
- if ( hasval( center ) ) {
- if ( hasval( center.prefSize.h ) ) {
- hSum += center.prefSize.h;
- }
- else {
- hSum = null;
- }
+ if (hasval(center)) {
+ if (hasval(center.prefSize.h)) {
+ hSum += center.prefSize.h;
}
-
- parentPrefSize.w = null;
- parentPrefSize.h = hSum;
- },
-
- updateChildViewports: function( children : LayoutEntry[], parentViewport : BoundsUnmodifiable ) {
- var topAxis : LayoutEntry = null;
- var bottomAxis : LayoutEntry = null;
- var center : LayoutEntry = null;
- for ( var c = 0; c < children.length; c++ ) {
- var child = children[ c ];
- switch ( child.layoutArg ) {
- case Side.TOP:
- if ( hasval( topAxis ) ) throw new Error( 'Timeline-layout can have at most one top-axis pane' );
- topAxis = child;
- break;
-
- case Side.BOTTOM:
- if ( hasval( bottomAxis ) ) throw new Error( 'Timeline-layout can have at most one bottom-axis pane' );
- bottomAxis = child;
- break;
-
- default:
- if ( hasval( center ) ) throw new Error( 'Timeline-layout can have at most one center pane' );
- center = child;
- break;
- }
+ else {
+ hSum = null;
}
+ }
- if ( hasval( topAxis ) ) {
- topAxis.viewport.setRect( parentViewport.i, parentViewport.jEnd - axisHeight, parentViewport.w, axisHeight );
+ parentPrefSize.w = null;
+ parentPrefSize.h = hSum;
+ },
+
+ updateChildViewports: function (children: LayoutEntry[], parentViewport: BoundsUnmodifiable) {
+ let topAxis: LayoutEntry = null;
+ let bottomAxis: LayoutEntry = null;
+ let center: LayoutEntry = null;
+ for (let c = 0; c < children.length; c++) {
+ const child = children[c];
+ switch (child.layoutArg) {
+ case Side.TOP:
+ if (hasval(topAxis)) {
+ throw new Error('Timeline-layout can have at most one top-axis pane');
+ }
+ topAxis = child;
+ break;
+
+ case Side.BOTTOM:
+ if (hasval(bottomAxis)) {
+ throw new Error('Timeline-layout can have at most one bottom-axis pane');
+ }
+ bottomAxis = child;
+ break;
+
+ default:
+ if (hasval(center)) {
+ throw new Error('Timeline-layout can have at most one center pane');
+ }
+ center = child;
+ break;
}
+ }
- if ( hasval( bottomAxis ) ) {
- var jBottomMax = ( hasval( topAxis ) ? topAxis.viewport.j : parentViewport.jEnd ) - axisHeight;
- bottomAxis.viewport.setRect( parentViewport.i, Math.min( jBottomMax, parentViewport.j ), parentViewport.w, axisHeight );
- }
+ if (hasval(topAxis)) {
+ topAxis.viewport.setRect(parentViewport.i, parentViewport.jEnd - axisHeight, parentViewport.w, axisHeight);
+ }
- if ( hasval( center ) ) {
- var jCenterEnd = ( hasval( topAxis ) ? topAxis.viewport.jStart : parentViewport.jEnd );
- var jCenterStart = ( hasval( bottomAxis ) ? bottomAxis.viewport.jEnd : parentViewport.jStart );
- center.viewport.setEdges( parentViewport.iStart, parentViewport.iEnd, jCenterStart, jCenterEnd );
- }
+ if (hasval(bottomAxis)) {
+ const jBottomMax = (hasval(topAxis) ? topAxis.viewport.j : parentViewport.jEnd) - axisHeight;
+ bottomAxis.viewport.setRect(parentViewport.i, Math.min(jBottomMax, parentViewport.j), parentViewport.w, axisHeight);
+ }
+
+ if (hasval(center)) {
+ const jCenterEnd = (hasval(topAxis) ? topAxis.viewport.jStart : parentViewport.jEnd);
+ const jCenterStart = (hasval(bottomAxis) ? bottomAxis.viewport.jEnd : parentViewport.jStart);
+ center.viewport.setEdges(parentViewport.iStart, parentViewport.iEnd, jCenterStart, jCenterEnd);
}
+ }
- };
- }
+ };
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/timeline/timeline_model.ts b/src/webglimpse/timeline/timeline_model.ts
index d8de19f..eff4ad5 100644
--- a/src/webglimpse/timeline/timeline_model.ts
+++ b/src/webglimpse/timeline/timeline_model.ts
@@ -27,1816 +27,2066 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
-
- export interface TimelineCursor {
- cursorGuid : string;
- labeledTimeseriesGuids : string[];
- lineColor : string;
- textColor : string;
- showVerticalLine : boolean;
- showHorizontalLine : boolean;
- showCursorText : boolean;
- }
-
-
-
- export interface TimelineAnnotation {
- annotationGuid : string;
- // time (x axis position) of annotation
- time_ISO8601? : string;
- // y axis position of annotion
- y? : number;
- label : string;
- styleGuid : string;
- }
-
-
- export interface TimelineTimeseries {
- timeseriesGuid : string;
- // supported hints:
- // lines, points, lines-and-points, bars, area
- uiHint? : string;
- // used for bars and area plots
- // determines the y axis value that the bars/area originate from
- baseline? : number;
- lineColor? : string;
- pointColor? : string;
- lineThickness? : number;
- pointSize? : number
- fragmentGuids? : string[];
- }
-
-
- export interface TimelineTimeseriesFragment {
- fragmentGuid : string;
- data? : number[];
- times_ISO8601? : string[];
-
- // undefined : user cannot adjust data points
- // 'y' : user can adjust y value of points, but not time value
- // 'xy : user can adjust both x (time) and y value of points
- userEditMode? : string;
- }
-
- export interface TimelineEvent {
- eventGuid : string;
- // the left side (earliest) time bound of the event box
- start_ISO8601 : string;
- // the right side (latest) time bound of the event box
- end_ISO8601 : string;
- // text to be displayed with the event
- label : string;
- // the minimum allowed value of start_ISO8601
- startLimit_ISO8601? : string;
- // the maximum allowed value of end_ISO8601
- endLimit_ISO8601? : string;
- // guid for icon shown to the left of the label text
- labelIcon? : string;
- // whether or not the event can be dragged / resized by user clicks
- userEditable? : boolean;
- // see timeline_event_style.ts (determines what icons the event displays)
- styleGuid? : string;
- // determines which events are drawn over others (higher numbers on top)
- order? : number;
- // distance between event box and top of timeline row
- topMargin? : number;
- // distance between event box and bottom of timeline row
- bottomMargin? : number;
- // text label color
- fgColor? : string;
- // event box color
- bgColor? : string;
- // event box secondary color used when displaying stiped background
- bgSecondaryColor? : string;
- // event box border color
- borderColor? : string;
- // secondary event box border color used when displaying stippled event borders
- borderSecondaryColor? : string;
- // portion at the top of the timeline row not considered by labelVAlign when placing text
- labelTopMargin? : number;
- // portion at the bottom of the timeline row not considered by labelVAlign when placing text
- labelBottomMargin? : number;
- // vertical alignment of label text (0 = bottom of row, 0.5 = middle of row, 1 = top of row)
- labelVAlign? : number;
- // relative position within the text label of the point considered the start of the label text (0 = bottom, 1 = top) this is the point positioned by labelVAlign
- // if labelVPos is not set, it defaults to labelVAlign's value, which is usually what is intended anyway
- labelVPos? : number;
- // vertical alignment of label text (0=left side, 0.5 = middle of event, 1=right side)
- labelHAlign? : number;
- // relative position within the text label of the point considered the start of the label text (0 = left side, 1 = right side) this is the point positioned by labelHAlign
- // if labelHPos is not set, it defaults to labelHAlign's value, which is usually what is intended anyway
- labelHPos? : number;
- // determines if the borders are dashed or not
- isBorderDashed? : boolean;
- // determines the fill pattern of the event ('solid', 'stripes')
- fillPattern? : string;
- }
-
-
- export interface TimelineRow {
- rowGuid : string;
- label : string;
- hidden? : boolean;
- rowHeight? : number;
- yMin? : number;
- yMax? : number;
- uiHint? : string;
- eventGuids? : string[];
- timeseriesGuids? : string[];
- annotationGuids? : string[];
- cursorGuid? : string;
- bgColor? : string;
- fgLabelColor? : string;
- bgLabelColor? : string;
- labelFont? : string;
- }
-
-
- export interface TimelineGroup {
- groupGuid : string;
- rollupGuid? : string;
- label : string;
- hidden? : boolean;
- collapsed? : boolean;
- rowGuids : string[];
- }
-
-
- export interface TimelineRoot {
- groupGuids : string[];
- topPinnedRowGuids : string[];
- bottomPinnedRowGuids : string[];
- maximizedRowGuids : string[];
- }
-
-
- export interface Timeline {
- cursors : TimelineCursor[];
- annotations : TimelineAnnotation[];
- timeseriesFragments : TimelineTimeseriesFragment[];
- timeseries : TimelineTimeseries[];
- events : TimelineEvent[];
- rows : TimelineRow[];
- groups : TimelineGroup[];
- root : TimelineRoot;
- }
-
-
-
- export class TimelineCursorModel {
- private _cursorGuid : string;
- // guids of timeseries to display values for at the selected time
- private _labeledTimeseriesGuids : OrderedStringSet;
- private _lineColor : Color;
- private _textColor : Color;
- private _showCursorText : boolean;
- private _showVerticalLine : boolean;
- private _showHorizontalLine : boolean;
- private _attrsChanged : Notification;
-
- constructor( cursor : TimelineCursor ) {
- this._cursorGuid = cursor.cursorGuid;
- this._attrsChanged = new Notification( );
- this.setAttrs( cursor );
- }
-
- get labeledTimeseriesGuids( ) : OrderedStringSet {
- return this._labeledTimeseriesGuids;
- }
-
- get cursorGuid( ) : string {
- return this._cursorGuid;
- }
-
- get attrsChanged( ) : Notification {
- return this._attrsChanged;
- }
-
- get lineColor( ) : Color {
- return this._lineColor;
- }
-
- get textColor( ) : Color {
- return this._textColor;
- }
-
- get showVerticalLine( ) : boolean {
- return this._showVerticalLine;
- }
-
- get showHorizontalLine( ) : boolean {
- return this._showHorizontalLine;
- }
-
- get showCursorText( ) : boolean {
- return this._showCursorText;
- }
-
- setAttrs( cursor : TimelineCursor ) {
- this._labeledTimeseriesGuids = new OrderedStringSet( cursor.labeledTimeseriesGuids || [] );
- this._lineColor = ( hasval( cursor.lineColor ) ? parseCssColor( cursor.lineColor ) : null );
- this._textColor = ( hasval( cursor.textColor ) ? parseCssColor( cursor.textColor ) : null );
- this._showVerticalLine = cursor.showVerticalLine;
- this._showHorizontalLine = cursor.showHorizontalLine;
- this._showCursorText = cursor.showCursorText;
- this._attrsChanged.fire( );
- }
-
- snapshot( ) : TimelineCursor {
- return {
- cursorGuid: this._cursorGuid,
- labeledTimeseriesGuids: this._labeledTimeseriesGuids.toArray( ),
- lineColor: ( hasval( this._lineColor ) ? this._lineColor.cssString : null ),
- textColor: ( hasval( this._textColor ) ? this._textColor.cssString : null ),
- showVerticalLine: this._showVerticalLine,
- showHorizontalLine: this._showHorizontalLine,
- showCursorText: this._showCursorText
- };
- }
- }
-
-
- export class TimelineAnnotationModel {
- private _annotationGuid : string;
- private _attrsChanged : Notification;
- private _time_PMILLIS : number;
- private _y : number;
- private _label : string;
- private _styleGuid : string;
-
- constructor( annotation : TimelineAnnotation ) {
- this._annotationGuid = annotation.annotationGuid;
- this._attrsChanged = new Notification( );
- this.setAttrs( annotation );
- }
-
- get annotationGuid( ) : string {
- return this._annotationGuid;
- }
-
- get attrsChanged( ) : Notification {
- return this._attrsChanged;
- }
-
- setLocation( time_PMILLIS : number, y : number ) {
- if ( time_PMILLIS !== this._time_PMILLIS || y !== this.y ) {
- this._y = y;
- this._time_PMILLIS = time_PMILLIS;
- this._attrsChanged.fire( );
+import { OrderedStringSet, OrderedSet } from '../util/ordered_set';
+import { Color, parseCssColor } from '../color';
+import { Notification, Notification2 } from '../util/notification';
+import { formatTime_ISO8601, order, hasval, parseTime_PMILLIS } from '../util/util';
+import { indexOf } from '../util/sorted_arrays';
+import { FillPattern } from './timeline_events_row';
+import { Axis1D } from '../plot/axis';
+import { Insets } from '../layout/inset_layout';
+
+
+export interface TimelineCursor {
+ cursorGuid: string;
+ labeledTimeseriesGuids: string[];
+ lineColor: string;
+ textColor: string;
+ showVerticalLine: boolean;
+ showHorizontalLine: boolean;
+ showCursorText: boolean;
+}
+
+
+
+export interface TimelineAnnotation {
+ annotationGuid: string;
+ // time (x axis position) of annotation
+ time_ISO8601?: string;
+ // y axis position of annotion
+ y?: number;
+ label: string;
+ styleGuid: string;
+ pickable?: boolean;
+}
+
+
+export interface TimelineTimeseries {
+ timeseriesGuid: string;
+ // supported hints:
+ // lines, points, lines-and-points, bars, area
+ uiHint?: string;
+ // used for bars and area plots
+ // determines the y axis value that the bars/area originate from
+ baseline?: number;
+ lineColor?: string;
+ pointColor?: string;
+ lineThickness?: number;
+ pointSize?: number;
+ dash?: number;
+ fragmentGuids?: string[];
+}
+
+
+export interface TimelineTimeseriesFragment {
+ fragmentGuid: string;
+ data?: number[];
+ times_ISO8601?: string[];
+
+ // undefined : user cannot adjust data points
+ // 'y' : user can adjust y value of points, but not time value
+ // 'xy : user can adjust both x (time) and y value of points
+ userEditMode?: string;
+}
+
+export interface TimelineEvent {
+ eventGuid: string;
+ // the left side (earliest) time bound of the event box
+ start_ISO8601: string;
+ // the right side (latest) time bound of the event box
+ end_ISO8601: string;
+ // text to be displayed with the event
+ label: string;
+ // the minimum allowed value of start_ISO8601
+ startLimit_ISO8601?: string;
+ // the maximum allowed value of end_ISO8601
+ endLimit_ISO8601?: string;
+ // guid for icon shown to the left of the label text
+ labelIcon?: string;
+ // whether or not the event can be dragged / resized by user clicks
+ userEditable?: boolean;
+ // see timeline_event_style.ts (determines what icons the event displays)
+ styleGuid?: string;
+ // determines which events are drawn over others (higher numbers on top)
+ order?: number;
+ // distance between event box and top of timeline row
+ topMargin?: number;
+ // distance between event box and bottom of timeline row
+ bottomMargin?: number;
+ // text label color
+ fgColor?: string;
+ // event box color
+ bgColor?: string;
+ // event box secondary color used when displaying stiped background
+ bgSecondaryColor?: string;
+ // event box border color
+ borderColor?: string;
+ // secondary event box border color used when displaying stippled event borders
+ borderSecondaryColor?: string;
+ // portion at the top of the timeline row not considered by labelVAlign when placing text
+ labelTopMargin?: number;
+ // portion at the bottom of the timeline row not considered by labelVAlign when placing text
+ labelBottomMargin?: number;
+ // vertical alignment of label text (0 = bottom of row, 0.5 = middle of row, 1 = top of row)
+ labelVAlign?: number;
+ // relative position within the text label of the point considered the start of the label text (0 = bottom, 1 = top) this is the point positioned by labelVAlign
+ // if labelVPos is not set, it defaults to labelVAlign's value, which is usually what is intended anyway
+ labelVPos?: number;
+ // vertical alignment of label text (0=left side, 0.5 = middle of event, 1=right side)
+ labelHAlign?: number;
+ // relative position within the text label of the point considered the start of the label text (0 = left side, 1 = right side) this is the point positioned by labelHAlign
+ // if labelHPos is not set, it defaults to labelHAlign's value, which is usually what is intended anyway
+ labelHPos?: number;
+ // determines if the borders are dashed or not
+ isBorderDashed?: boolean;
+ // determines the fill pattern of the event ('solid', 'stripes')
+ fillPattern?: string;
+}
+
+
+export interface TimelineRow {
+ rowGuid: string;
+ label: string;
+ truncate?: boolean;
+ hidden?: boolean;
+ rowHeight?: number;
+ yMin?: number;
+ yMax?: number;
+ uiHint?: string;
+ eventGuids?: string[];
+ timeseriesGuids?: string[];
+ annotationGuids?: string[];
+ cursorGuid?: string;
+ bgColor?: string;
+ fgLabelColor?: string;
+ bgLabelColor?: string;
+ labelFont?: string;
+ cursor?: string;
+ highlighted?: boolean;
+ highlightColor?: Color;
+ highlightWidth?: number;
+ highlightInsets?: Insets;
+}
+
+
+export interface TimelineGroup {
+ groupGuid: string;
+ rollupGuid?: string;
+ label: string;
+ hidden?: boolean;
+ collapsed?: boolean;
+ highlighted?: boolean;
+ highlightColor?: Color;
+ highlightWidth?: number;
+ highlightInsets?: Insets;
+ dashPattern?: number;
+ dashLength?: number;
+ rowGuids: string[];
+ labelFont?: string;
+ cursor?: string;
+}
+
+
+export interface TimelineRoot {
+ groupGuids: string[];
+ topPinnedRowGuids: string[];
+ bottomPinnedRowGuids: string[];
+ maximizedRowGuids: string[];
+}
+
+
+export interface Timeline {
+ cursors: TimelineCursor[];
+ annotations: TimelineAnnotation[];
+ timeseriesFragments: TimelineTimeseriesFragment[];
+ timeseries: TimelineTimeseries[];
+ events: TimelineEvent[];
+ rows: TimelineRow[];
+ groups: TimelineGroup[];
+ root: TimelineRoot;
+}
+
+
+
+export class TimelineCursorModel {
+ private _cursorGuid: string;
+ // guids of timeseries to display values for at the selected time
+ private _labeledTimeseriesGuids: OrderedStringSet;
+ private _lineColor: Color;
+ private _textColor: Color;
+ private _showCursorText: boolean;
+ private _showVerticalLine: boolean;
+ private _showHorizontalLine: boolean;
+ private _attrsChanged: Notification;
+
+ constructor(cursor: TimelineCursor) {
+ this._cursorGuid = cursor.cursorGuid;
+ this._attrsChanged = new Notification();
+ this.setAttrs(cursor);
+ }
+
+ get labeledTimeseriesGuids(): OrderedStringSet {
+ return this._labeledTimeseriesGuids;
+ }
+
+ get cursorGuid(): string {
+ return this._cursorGuid;
+ }
+
+ get attrsChanged(): Notification {
+ return this._attrsChanged;
+ }
+
+ get lineColor(): Color {
+ return this._lineColor;
+ }
+
+ get textColor(): Color {
+ return this._textColor;
+ }
+
+ get showVerticalLine(): boolean {
+ return this._showVerticalLine;
+ }
+
+ get showHorizontalLine(): boolean {
+ return this._showHorizontalLine;
+ }
+
+ get showCursorText(): boolean {
+ return this._showCursorText;
+ }
+
+ setAttrs(cursor: TimelineCursor) {
+ this._labeledTimeseriesGuids = new OrderedStringSet(cursor.labeledTimeseriesGuids || []);
+ this._lineColor = (hasval(cursor.lineColor) ? parseCssColor(cursor.lineColor) : null);
+ this._textColor = (hasval(cursor.textColor) ? parseCssColor(cursor.textColor) : null);
+ this._showVerticalLine = cursor.showVerticalLine;
+ this._showHorizontalLine = cursor.showHorizontalLine;
+ this._showCursorText = cursor.showCursorText;
+ this._attrsChanged.fire();
+ }
+
+ snapshot(): TimelineCursor {
+ return {
+ cursorGuid: this._cursorGuid,
+ labeledTimeseriesGuids: this._labeledTimeseriesGuids.toArray(),
+ lineColor: (hasval(this._lineColor) ? this._lineColor.cssString : null),
+ textColor: (hasval(this._textColor) ? this._textColor.cssString : null),
+ showVerticalLine: this._showVerticalLine,
+ showHorizontalLine: this._showHorizontalLine,
+ showCursorText: this._showCursorText
+ };
+ }
+}
+
+
+export class TimelineAnnotationModel {
+ private _annotationGuid: string;
+ private _attrsChanged: Notification;
+ private _time_PMILLIS: number;
+ private _y: number;
+ private _label: string;
+ private _styleGuid: string;
+ private _pickable: boolean;
+
+ constructor(annotation: TimelineAnnotation) {
+ this._annotationGuid = annotation.annotationGuid;
+ this._attrsChanged = new Notification();
+ this.setAttrs(annotation);
+ }
+
+ get annotationGuid(): string {
+ return this._annotationGuid;
+ }
+
+ get attrsChanged(): Notification {
+ return this._attrsChanged;
+ }
+
+ setLocation(time_PMILLIS: number, y: number) {
+ if (time_PMILLIS !== this._time_PMILLIS || y !== this.y) {
+ this._y = y;
+ this._time_PMILLIS = time_PMILLIS;
+ this._attrsChanged.fire();
- }
}
-
- get time_PMILLIS( ) : number {
- return this._time_PMILLIS;
+ }
+
+ get time_PMILLIS(): number {
+ return this._time_PMILLIS;
+ }
+
+ set time_PMILLIS(time_PMILLIS: number) {
+ if (time_PMILLIS !== this._time_PMILLIS) {
+ this._time_PMILLIS = time_PMILLIS;
+ this._attrsChanged.fire();
}
-
- set time_PMILLIS( time_PMILLIS : number ) {
- if ( time_PMILLIS !== this._time_PMILLIS ) {
- this._time_PMILLIS = time_PMILLIS;
- this._attrsChanged.fire( );
- }
+ }
+
+ get y(): number {
+ return this._y;
+ }
+
+ set y(y: number) {
+ if (y !== this.y) {
+ this._y = y;
+ this._attrsChanged.fire();
}
-
- get y( ) : number {
- return this._y;
+ }
+
+ get label(): string {
+ return this._label;
+ }
+
+ set label(label: string) {
+ if (label !== this.label) {
+ this._label = label;
+ this._attrsChanged.fire();
}
-
- set y( y : number ) {
- if ( y !== this.y ) {
- this._y = y;
- this._attrsChanged.fire( );
- }
+ }
+
+ get styleGuid(): string {
+ return this._styleGuid;
+ }
+
+ set styleGuid(styleGuid: string) {
+ if (styleGuid !== this.styleGuid) {
+ this._styleGuid = styleGuid;
+ this._attrsChanged.fire();
}
-
- get label( ) : string {
- return this._label;
+ }
+
+ get pickable(): boolean {
+ return this._pickable;
+ }
+
+ set pickable(pickable: boolean) {
+ if (pickable !== this.pickable) {
+ this._pickable = pickable;
+ this._attrsChanged.fire();
}
-
- set label( label : string ) {
- if ( label !== this.label ) {
- this._label = label;
- this._attrsChanged.fire( );
- }
+ }
+
+ setAttrs(annotation: TimelineAnnotation) {
+ // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
+ this._time_PMILLIS = hasval(annotation.time_ISO8601) ? parseTime_PMILLIS(annotation.time_ISO8601) : undefined;
+ this._y = annotation.y;
+ this._label = annotation.label;
+ this._styleGuid = annotation.styleGuid;
+ this._pickable = hasval(annotation.pickable) ? annotation.pickable : true;
+ this._attrsChanged.fire();
+ }
+
+ snapshot(): TimelineAnnotation {
+ return {
+ annotationGuid: this._annotationGuid,
+ label: this._label,
+ styleGuid: this._styleGuid,
+ time_ISO8601: formatTime_ISO8601(this._time_PMILLIS),
+ y: this._y,
+ pickable: this._pickable,
+ };
+ }
+}
+
+
+export class TimelineTimeseriesModel {
+ private _timeseriesGuid: string;
+ private _attrsChanged: Notification;
+ private _uiHint: string;
+ private _baseline: number;
+ private _lineColor: Color;
+ private _pointColor: Color;
+ private _lineThickness: number;
+ private _pointSize: number;
+ private _dash: number;
+ private _fragmentGuids: OrderedStringSet;
+
+ constructor(timeseries: TimelineTimeseries) {
+ this._timeseriesGuid = timeseries.timeseriesGuid;
+ this._attrsChanged = new Notification();
+ this.setAttrs(timeseries);
+ this._fragmentGuids = new OrderedStringSet(timeseries.fragmentGuids || []);
+ }
+
+ get timeseriesGuid(): string {
+ return this._timeseriesGuid;
+ }
+
+ get attrsChanged(): Notification {
+ return this._attrsChanged;
+ }
+
+ setAttrs(timeseries: TimelineTimeseries) {
+ // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
+ this._uiHint = timeseries.uiHint;
+ this._baseline = timeseries.baseline;
+ this._lineColor = (hasval(timeseries.lineColor) ? parseCssColor(timeseries.lineColor) : null);
+ this._pointColor = (hasval(timeseries.pointColor) ? parseCssColor(timeseries.pointColor) : null);
+ this._lineThickness = timeseries.lineThickness;
+ this._pointSize = timeseries.pointSize;
+ this._dash = timeseries.dash;
+ this._fragmentGuids = new OrderedStringSet(timeseries.fragmentGuids || []);
+ this._attrsChanged.fire();
+ }
+
+ get baseline(): number {
+ return this._baseline;
+ }
+
+ set baseline(baseline: number) {
+ if (baseline !== this._baseline) {
+ this._baseline = baseline;
+ this._attrsChanged.fire();
}
-
- get styleGuid( ) : string {
- return this._styleGuid;
+ }
+
+ get lineColor(): Color {
+ return this._lineColor;
+ }
+
+ set lineColor(lineColor: Color) {
+ if (lineColor !== this._lineColor) {
+ this._lineColor = lineColor;
+ this._attrsChanged.fire();
}
-
- set styleGuid( styleGuid : string ) {
- if ( styleGuid !== this.styleGuid ) {
- this._styleGuid = styleGuid;
- this._attrsChanged.fire( );
- }
+ }
+
+ get pointColor(): Color {
+ return this._pointColor;
+ }
+
+ set pointColor(pointColor: Color) {
+ if (pointColor !== this._pointColor) {
+ this._pointColor = pointColor;
+ this._attrsChanged.fire();
}
+ }
- setAttrs( annotation : TimelineAnnotation ) {
- // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
- this._time_PMILLIS = hasval( annotation.time_ISO8601 ) ? parseTime_PMILLIS( annotation.time_ISO8601 ) : undefined;
- this._y = annotation.y;
- this._label = annotation.label;
- this._styleGuid = annotation.styleGuid;
- this._attrsChanged.fire( );
+ get lineThickness(): number {
+ return this._lineThickness;
+ }
+
+ set lineThickness(lineThickness: number) {
+ if (lineThickness !== this._lineThickness) {
+ this._lineThickness = lineThickness;
+ this._attrsChanged.fire();
}
-
- snapshot( ) : TimelineAnnotation {
- return {
- annotationGuid: this._annotationGuid,
- label: this._label,
- styleGuid: this._styleGuid,
- time_ISO8601 : formatTime_ISO8601( this._time_PMILLIS ),
- y: this._y,
- };
+ }
+
+ get pointSize(): number {
+ return this._pointSize;
+ }
+
+ set pointSize(pointSize: number) {
+ if (pointSize !== this._pointSize) {
+ this._pointSize = pointSize;
+ this._attrsChanged.fire();
}
}
+ get dash(): number {
+ return this._dash;
+ }
- export class TimelineTimeseriesModel {
- private _timeseriesGuid : string;
- private _attrsChanged : Notification;
- private _uiHint : string;
- private _baseline : number;
- private _lineColor : Color;
- private _pointColor : Color;
- private _lineThickness : number;
- private _pointSize : number;
- private _fragmentGuids : OrderedStringSet;
+ set dash(dash: number) {
+ if (dash !== this._dash) {
+ this._dash = dash;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get uiHint(): string {
+ return this._uiHint;
+ }
- constructor( timeseries : TimelineTimeseries ) {
- this._timeseriesGuid = timeseries.timeseriesGuid;
- this._attrsChanged = new Notification( );
- this.setAttrs( timeseries );
- this._fragmentGuids = new OrderedStringSet( timeseries.fragmentGuids || [] );
+ set uiHint(uiHint: string) {
+ if (uiHint !== this._uiHint) {
+ this._uiHint = uiHint;
+ this._attrsChanged.fire();
}
+ }
- get timeseriesGuid( ) : string {
- return this._timeseriesGuid;
+ get fragmentGuids(): OrderedStringSet {
+ return this._fragmentGuids;
+ }
+
+ set fragmentGuids(fragmentGuids: OrderedStringSet) {
+ if (fragmentGuids !== this._fragmentGuids) {
+ this._fragmentGuids = fragmentGuids;
+ this._attrsChanged.fire();
}
+ }
+
+ snapshot(): TimelineTimeseries {
+ return {
+ timeseriesGuid: this._timeseriesGuid,
+ uiHint: this._uiHint,
+ baseline: this._baseline,
+ lineColor: (hasval(this._lineColor) ? this._lineColor.cssString : null),
+ pointColor: (hasval(this._pointColor) ? this._pointColor.cssString : null),
+ lineThickness: this._lineThickness,
+ pointSize: this._pointSize,
+ dash: this._dash,
+ fragmentGuids: this._fragmentGuids.toArray(),
+ };
+ }
+}
+
+
+export class TimelineTimeseriesFragmentModel {
+ private _fragmentGuid: string;
+ // notification provides the start and end indexes of the modified range
+ // start index is inclusive, end index is exclusive
+ private _dataChanged: Notification2;
+ private _attrsChanged: Notification;
+ private _userEditMode: string;
+ private _data: number[];
+ private _times_PMILLIS: number[];
+
+ constructor(fragment: TimelineTimeseriesFragment) {
+ this._fragmentGuid = fragment.fragmentGuid;
+ this._attrsChanged = new Notification();
+ this._dataChanged = new Notification2();
+ this.setAttrs(fragment);
+ }
+
+ get fragmentGuid(): string {
+ return this._fragmentGuid;
+ }
+
+ get dataChanged(): Notification2 {
+ return this._dataChanged;
+ }
- get attrsChanged( ) : Notification {
- return this._attrsChanged;
+ setAttrs(fragment: TimelineTimeseriesFragment) {
+ this._userEditMode = fragment.userEditMode;
+ this._times_PMILLIS = hasval(fragment.times_ISO8601) ? fragment.times_ISO8601.map(parseTime_PMILLIS) : [];
+ this._data = hasval(fragment.data) ? fragment.data.slice() : [];
+ this._dataChanged.fire(0, this._data.length);
+ this._attrsChanged.fire();
+ }
+
+ get data(): number[] {
+ return this._data;
+ }
+
+ set data(data: number[]) {
+ if (data !== this._data) {
+ this._data = data;
+ this._dataChanged.fire(0, this._data.length);
}
+ }
+
+ get times_PMILLIS(): number[] {
+ return this._times_PMILLIS;
+ }
- setAttrs( timeseries : TimelineTimeseries ) {
- // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
- this._uiHint = timeseries.uiHint;
- this._baseline = timeseries.baseline;
- this._lineColor = ( hasval( timeseries.lineColor ) ? parseCssColor( timeseries.lineColor ) : null );
- this._pointColor = ( hasval( timeseries.pointColor ) ? parseCssColor( timeseries.pointColor ) : null );
- this._lineThickness = timeseries.lineThickness;
- this._pointSize = timeseries.pointSize;
- this._fragmentGuids = new OrderedStringSet( timeseries.fragmentGuids || [] );
- this._attrsChanged.fire( );
+ // Time should only be modified in a way which keeps the _times_PMILLIS
+ // array sorted. This is currently not enforced by the model.
+ set times_PMILLIS(times_PMILLIS: number[]) {
+ if (times_PMILLIS !== this._times_PMILLIS) {
+ this._times_PMILLIS = times_PMILLIS;
+ this._dataChanged.fire(0, this._data.length);
}
+ }
- get baseline( ) : number {
- return this._baseline;
+ // Time should only be modified in a way which keeps the _times_PMILLIS
+ // array sorted. This is currently not enforced by the model.
+ setAllData(data: number[], times_PMILLIS: number[]) {
+ if (data !== this._data || times_PMILLIS !== this._times_PMILLIS) {
+ this._data = data;
+ this._times_PMILLIS = times_PMILLIS;
+ this._dataChanged.fire(0, this._data.length);
}
+ }
- set baseline( baseline : number ) {
- if ( baseline !== this._baseline ) {
- this._baseline = baseline;
- this._attrsChanged.fire( );
+ // Handles adjusting the _times_PMILLIS and _data arrays if the new time
+ // requires them to be rearranged to stay in time order. Returns the new
+ // index assigned to the data point.
+ setData(index: number, value: number, time?: number): number {
+ if (this._data[index] !== value || (hasval(time) && this._times_PMILLIS[index] !== time)) {
+ if (hasval(time)) {
+ // the new time value would maintain the sorted order of the array
+ if ((index === 0 || time > this._times_PMILLIS[index - 1]) &&
+ (index === this._times_PMILLIS.length - 1 || time < this._times_PMILLIS[index + 1])) {
+ this._times_PMILLIS[index] = time;
+ this._data[index] = value;
+ this._dataChanged.fire(index, index + 1);
+ }
+ else {
+ // remove the current point at index
+ this._times_PMILLIS.splice(index, 1);
+ this._data.splice(index, 1);
+
+ // find the index to reinsert new data at
+ index = indexOf(this._times_PMILLIS, time);
+ if (index < 0) {
+ index = -index - 1;
+ }
+
+ this._times_PMILLIS.splice(index, 0, time);
+ this._data.splice(index, 0, value);
+ this._dataChanged.fire(index, index + 1);
+ }
+ }
+ else {
+ this._data[index] = value;
+ this._dataChanged.fire(index, index + 1);
}
}
- get lineColor( ) : Color {
- return this._lineColor;
+ return index;
+ }
+
+ get start_PMILLIS(): number {
+ return this._times_PMILLIS[0];
+ }
+
+ get end_PMILLIS(): number {
+ return this._times_PMILLIS.slice(-1)[0];
+ }
+
+ get userEditMode(): string {
+ return this._userEditMode;
+ }
+
+ set userEditMode(userEditMode: string) {
+ if (userEditMode !== this._userEditMode) {
+ this._userEditMode = userEditMode;
+ this._attrsChanged.fire();
}
+ }
+
+ snapshot(): TimelineTimeseriesFragment {
+ return {
+ userEditMode: this._userEditMode,
+ fragmentGuid: this._fragmentGuid,
+ data: this._data.slice(),
+ times_ISO8601: this._times_PMILLIS.map(formatTime_ISO8601)
+ };
+ }
+}
+
+
+export class TimelineEventModel {
+ private _eventGuid: string;
+ private _attrsChanged: Notification;
+ private _startLimit_PMILLIS: number;
+ private _endLimit_PMILLIS: number;
+ private _start_PMILLIS: number;
+ private _end_PMILLIS: number;
+ private _label: string;
+ private _labelIcon: string;
+ private _userEditable: boolean;
+ private _styleGuid: string;
+ private _order: number;
+ private _topMargin: number;
+ private _bottomMargin: number;
+ private _fgColor: Color;
+ private _bgColor: Color;
+ private _bgSecondaryColor: Color;
+ private _borderColor: Color;
+ private _borderSecondaryColor: Color;
+ private _labelTopMargin: number;
+ private _labelBottomMargin: number;
+ private _labelVAlign: number;
+ private _labelVPos: number;
+ private _labelHAlign: number;
+ private _labelHPos: number;
+ private _isBorderDashed: boolean;
+ private _fillPattern: FillPattern;
+
+ constructor(event: TimelineEvent) {
+ this._eventGuid = event.eventGuid;
+ this._attrsChanged = new Notification();
+ this.setAttrs(event);
+ }
- set lineColor( lineColor : Color ) {
- if ( lineColor !== this._lineColor ) {
- this._lineColor = lineColor;
- this._attrsChanged.fire( );
+ get eventGuid(): string {
+ return this._eventGuid;
+ }
+
+ get attrsChanged(): Notification {
+ return this._attrsChanged;
+ }
+
+ setAttrs(event: TimelineEvent) {
+ // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
+ this._startLimit_PMILLIS = (hasval(event.startLimit_ISO8601) ? Math.trunc(parseTime_PMILLIS(event.startLimit_ISO8601)) : null);
+ this._endLimit_PMILLIS = (hasval(event.endLimit_ISO8601) ? Math.trunc(parseTime_PMILLIS(event.endLimit_ISO8601)) : null);
+ this._start_PMILLIS = Math.trunc(parseTime_PMILLIS(event.start_ISO8601));
+ this._end_PMILLIS = Math.trunc(parseTime_PMILLIS(event.end_ISO8601));
+ this._label = event.label;
+ this._labelIcon = event.labelIcon;
+ this._userEditable = (hasval(event.userEditable) ? event.userEditable : false);
+ this._styleGuid = event.styleGuid;
+ this._order = event.order;
+ this._topMargin = event.topMargin;
+ this._bottomMargin = event.bottomMargin;
+ this._fgColor = (hasval(event.fgColor) ? parseCssColor(event.fgColor) : null);
+ this._bgColor = (hasval(event.bgColor) ? parseCssColor(event.bgColor) : null);
+ this._bgSecondaryColor = (hasval(event.bgSecondaryColor) ? parseCssColor(event.bgSecondaryColor) : null);
+ this._borderColor = (hasval(event.borderColor) ? parseCssColor(event.borderColor) : null);
+ this._borderSecondaryColor = (hasval(event.borderSecondaryColor) ? parseCssColor(event.borderSecondaryColor) : null);
+ this._labelTopMargin = event.labelTopMargin;
+ this._labelBottomMargin = event.labelBottomMargin;
+ this._labelVAlign = event.labelVAlign;
+ this._labelVPos = event.labelVPos;
+ this._labelHAlign = event.labelHAlign;
+ this._labelHPos = event.labelHPos;
+ this._isBorderDashed = (hasval(event.isBorderDashed) ? event.isBorderDashed : false);
+ this._fillPattern = (hasval(event.fillPattern) ? FillPattern[event.fillPattern] : FillPattern.solid);
+ this._attrsChanged.fire();
+ }
+
+ setInterval(start_PMILLIS: number, end_PMILLIS: number) {
+ if (start_PMILLIS !== this._start_PMILLIS || end_PMILLIS !== this._end_PMILLIS) {
+
+ const initial_start_PMILLIS = this._start_PMILLIS;
+ const initial_end_PMILLIS = this._end_PMILLIS;
+
+ const underStartLimit = hasval(this._startLimit_PMILLIS) && start_PMILLIS < this._startLimit_PMILLIS;
+ const overEndLimit = hasval(this._endLimit_PMILLIS) && end_PMILLIS > this._endLimit_PMILLIS;
+ const duration_PMILLIS = end_PMILLIS - start_PMILLIS;
+ const durationLimit_PMILLIS = this._endLimit_PMILLIS - this._startLimit_PMILLIS;
+
+ // If both limits are present and the event is larger than the total distance between them
+ // then shrink the event to fit between the limits.
+ if (hasval(this._startLimit_PMILLIS) && hasval(this._endLimit_PMILLIS) && durationLimit_PMILLIS < duration_PMILLIS) {
+ this._start_PMILLIS = Math.trunc(this._startLimit_PMILLIS);
+ this._end_PMILLIS = Math.trunc(this._endLimit_PMILLIS);
+ }
+ // Otherwise shift the event to comply with the limits without adjusting its total duration
+ else if (underStartLimit) {
+ this._start_PMILLIS = Math.trunc(this._startLimit_PMILLIS);
+ this._end_PMILLIS = Math.trunc(this._start_PMILLIS + duration_PMILLIS);
+ }
+ else if (overEndLimit) {
+ this._end_PMILLIS = Math.trunc(this._endLimit_PMILLIS);
+ this._start_PMILLIS = Math.trunc(this._end_PMILLIS - duration_PMILLIS);
+ }
+ else {
+ this._end_PMILLIS = Math.trunc(end_PMILLIS);
+ this._start_PMILLIS = Math.trunc(start_PMILLIS);
+ }
+
+ // its possible due to the limits that the values didn't actually change
+ // only fire attrsChanged if one of the values did actually change
+ if (initial_start_PMILLIS !== this._start_PMILLIS || initial_end_PMILLIS !== this._end_PMILLIS) {
+ this._attrsChanged.fire();
}
}
+ }
+
+ private limit_start_PMILLIS(start_PMILLIS: number) {
+ return hasval(this._startLimit_PMILLIS) ? Math.max(start_PMILLIS, this._startLimit_PMILLIS) : start_PMILLIS;
+ }
+
+ private limit_end_PMILLIS(end_PMILLIS: number) {
+ return hasval(this._endLimit_PMILLIS) ? Math.min(end_PMILLIS, this._endLimit_PMILLIS) : end_PMILLIS;
+ }
- get pointColor( ) : Color {
- return this._pointColor;
+ get start_PMILLIS(): number {
+ return this._start_PMILLIS;
+ }
+
+ set start_PMILLIS(start_PMILLIS: number) {
+ if (start_PMILLIS !== this._start_PMILLIS) {
+ this._start_PMILLIS = Math.trunc(this.limit_start_PMILLIS(start_PMILLIS));
+ this._attrsChanged.fire();
}
+ }
- set pointColor( pointColor : Color ) {
- if ( pointColor !== this._pointColor ) {
- this._pointColor = pointColor;
- this._attrsChanged.fire( );
- }
+ get end_PMILLIS(): number {
+ return this._end_PMILLIS;
+ }
+
+ set end_PMILLIS(end_PMILLIS: number) {
+ if (end_PMILLIS !== this._end_PMILLIS) {
+ this._end_PMILLIS = Math.trunc(this.limit_end_PMILLIS(end_PMILLIS));
+ this._attrsChanged.fire();
+ }
+ }
+
+ get startLimit_PMILLIS(): number {
+ return this._startLimit_PMILLIS;
+ }
+
+ set startLimit_PMILLIS(startLimit_PMILLIS: number) {
+ if (startLimit_PMILLIS !== this._startLimit_PMILLIS) {
+ this._startLimit_PMILLIS = Math.trunc(startLimit_PMILLIS);
+ this._start_PMILLIS = Math.trunc(this.limit_start_PMILLIS(this._start_PMILLIS));
+ this._attrsChanged.fire();
+ }
+ }
+
+ get endLimit_PMILLIS(): number {
+ return this._endLimit_PMILLIS;
+ }
+
+ set endLimit_PMILLIS(endLimit_PMILLIS: number) {
+ if (endLimit_PMILLIS !== this._endLimit_PMILLIS) {
+ this._endLimit_PMILLIS = Math.trunc(endLimit_PMILLIS);
+ this._end_PMILLIS = Math.trunc(this.limit_end_PMILLIS(this._end_PMILLIS));
+ this._attrsChanged.fire();
+ }
+ }
+
+
+ get label(): string {
+ return this._label;
+ }
+
+ set label(label: string) {
+ if (label !== this._label) {
+ this._label = label;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get labelIcon(): string {
+ return this._labelIcon;
+ }
+
+ set labelIcon(labelIcon: string) {
+ if (labelIcon !== this._labelIcon) {
+ this._labelIcon = labelIcon;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get userEditable(): boolean {
+ return this._userEditable;
+ }
+
+ set userEditable(userEditable: boolean) {
+ if (userEditable !== this._userEditable) {
+ this._userEditable = userEditable;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get styleGuid(): string {
+ return this._styleGuid;
+ }
+
+ set styleGuid(styleGuid: string) {
+ if (styleGuid !== this._styleGuid) {
+ this._styleGuid = styleGuid;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get order(): number {
+ return this._order;
+ }
+
+ set order(orderVal: number) {
+ if (orderVal !== this._order) {
+ this._order = orderVal;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get topMargin(): number {
+ return this._topMargin;
+ }
+
+ set topMargin(topMargin: number) {
+ if (topMargin !== this._topMargin) {
+ this._topMargin = topMargin;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get bottomMargin(): number {
+ return this._bottomMargin;
+ }
+
+ set bottomMargin(bottomMargin: number) {
+ if (bottomMargin !== this._bottomMargin) {
+ this._bottomMargin = bottomMargin;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get fgColor(): Color {
+ return this._fgColor;
+ }
+
+ set fgColor(fgColor: Color) {
+ if (fgColor !== this._fgColor) {
+ this._fgColor = fgColor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get bgColor(): Color {
+ return this._bgColor;
+ }
+
+ set bgColor(bgColor: Color) {
+ if (bgColor !== this._bgColor) {
+ this._bgColor = bgColor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get bgSecondaryColor(): Color {
+ return this._bgSecondaryColor;
+ }
+
+ set bgSecondaryColor(bgSecondaryColor: Color) {
+ if (bgSecondaryColor !== this._bgSecondaryColor) {
+ this._bgSecondaryColor = bgSecondaryColor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get borderColor(): Color {
+ return this._borderColor;
+ }
+
+ set borderColor(borderColor: Color) {
+ if (borderColor !== this._borderColor) {
+ this._borderColor = borderColor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get borderSecondaryColor(): Color {
+ return this._borderSecondaryColor;
+ }
+
+ set borderSecondaryColor(borderSecondaryColor: Color) {
+ if (borderSecondaryColor !== this._borderSecondaryColor) {
+ this._borderSecondaryColor = borderSecondaryColor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get labelTopMargin(): number {
+ return this._labelTopMargin;
+ }
+
+ set labelTopMargin(labelTopMargin: number) {
+ if (labelTopMargin !== this._labelTopMargin) {
+ this._labelTopMargin = labelTopMargin;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get labelBottomMargin(): number {
+ return this._labelBottomMargin;
+ }
+
+ set labelBottomMargin(labelBottomMargin: number) {
+ if (labelBottomMargin !== this._labelBottomMargin) {
+ this._labelBottomMargin = labelBottomMargin;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get labelVAlign(): number {
+ return this._labelVAlign;
+ }
+
+ set labelVAlign(labelVAlign: number) {
+ if (labelVAlign !== this._labelVAlign) {
+ this._labelVAlign = labelVAlign;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get labelVPos(): number {
+ return this._labelVPos;
+ }
+
+ set labelVPos(labelVPos: number) {
+ if (labelVPos !== this._labelVPos) {
+ this._labelVPos = labelVPos;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get labelHAlign(): number {
+ return this._labelHAlign;
+ }
+
+ set labelHAlign(labelHAlign: number) {
+ if (labelHAlign !== this._labelHAlign) {
+ this._labelHAlign = labelHAlign;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get labelHPos(): number {
+ return this._labelHPos;
+ }
+
+ set labelHPos(labelHPos: number) {
+ if (labelHPos !== this._labelHPos) {
+ this._labelHPos = labelHPos;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get isBorderDashed(): boolean {
+ return this._isBorderDashed;
+ }
+
+ set isBorderDashed(isBorderDashed: boolean) {
+ if (isBorderDashed !== this._isBorderDashed) {
+ this._isBorderDashed = isBorderDashed;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get fillPattern(): FillPattern {
+ return this._fillPattern;
+ }
+
+ set fillPattern(fillPattern: FillPattern) {
+ if (fillPattern !== this._fillPattern) {
+ this._fillPattern = fillPattern;
+ this._attrsChanged.fire();
+ }
+ }
+
+ snapshot(): TimelineEvent {
+ return {
+ eventGuid: this._eventGuid,
+ startLimit_ISO8601: (hasval(this._startLimit_PMILLIS) ? formatTime_ISO8601(this._startLimit_PMILLIS) : null),
+ endLimit_ISO8601: (hasval(this._endLimit_PMILLIS) ? formatTime_ISO8601(this._endLimit_PMILLIS) : null),
+ start_ISO8601: formatTime_ISO8601(this._start_PMILLIS),
+ end_ISO8601: formatTime_ISO8601(this._end_PMILLIS),
+ label: this._label,
+ labelIcon: this._labelIcon,
+ userEditable: this._userEditable,
+ styleGuid: this._styleGuid,
+ order: this._order,
+ topMargin: this._topMargin,
+ bottomMargin: this._bottomMargin,
+ bgColor: (hasval(this._bgColor) ? this._bgColor.cssString : null),
+ bgSecondaryColor: (hasval(this._bgSecondaryColor) ? this._bgSecondaryColor.cssString : null),
+ fgColor: (hasval(this._fgColor) ? this._fgColor.cssString : null),
+ borderColor: (hasval(this._borderColor) ? this._borderColor.cssString : null),
+ borderSecondaryColor: (hasval(this._borderSecondaryColor) ? this.borderSecondaryColor.cssString : null),
+ labelTopMargin: this._labelTopMargin,
+ labelBottomMargin: this._labelBottomMargin,
+ labelVAlign: this._labelVAlign,
+ labelVPos: this._labelVPos,
+ labelHAlign: this._labelHAlign,
+ labelHPos: this._labelHPos,
+ isBorderDashed: this._isBorderDashed,
+ fillPattern: FillPattern[this._fillPattern]
+ };
+ }
+}
+
+
+export class TimelineRowModel {
+ private _rowGuid: string;
+ private _attrsChanged: Notification;
+ private _rowHeight: number;
+ private _hidden: boolean;
+ private _label: string;
+ private _truncate: boolean;
+ private _uiHint: string;
+ private _eventGuids: OrderedStringSet;
+ private _timeseriesGuids: OrderedStringSet;
+ private _annotationGuids: OrderedStringSet;
+ private _cursorGuid: string;
+ private _fgLabelColor: Color;
+ private _bgLabelColor: Color;
+ private _labelFont: string;
+ private _dataAxis: Axis1D;
+ private _bgColor: Color;
+ private _cursor: string;
+ private _highlighted: boolean;
+ private _highlightColor: Color;
+ private _highlightWidth: number;
+ private _highlightInsets: Insets;
+
+ constructor(row: TimelineRow) {
+ this._rowGuid = row.rowGuid;
+ this._attrsChanged = new Notification();
+
+ const min: number = hasval(row.yMin) ? row.yMin : 0;
+ const max: number = hasval(row.yMax) ? row.yMax : 1;
+ this._dataAxis = new Axis1D(min, max);
+
+ this.setAttrs(row);
+ this._eventGuids = new OrderedStringSet(row.eventGuids || []);
+ this._timeseriesGuids = new OrderedStringSet(row.timeseriesGuids || []);
+ this._annotationGuids = new OrderedStringSet(row.annotationGuids || []);
+ }
+
+ get rowGuid(): string {
+ return this._rowGuid;
+ }
+
+ get attrsChanged(): Notification {
+ return this._attrsChanged;
+ }
+
+ setAttrs(row: TimelineRow) {
+ // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
+ this._label = row.label;
+ this._truncate = row.truncate;
+ this._uiHint = row.uiHint;
+ this._hidden = row.hidden;
+ this._rowHeight = row.rowHeight;
+ this._cursorGuid = row.cursorGuid;
+ this._bgColor = (hasval(row.bgColor) ? parseCssColor(row.bgColor) : null);
+ this._fgLabelColor = (hasval(row.fgLabelColor) ? parseCssColor(row.fgLabelColor) : null);
+ this._bgLabelColor = (hasval(row.bgLabelColor) ? parseCssColor(row.bgLabelColor) : null);
+ this._labelFont = row.labelFont;
+ this._cursor = row.cursor;
+ this._highlighted = hasval(row.highlighted) ? row.highlighted : false;
+ this._highlightColor = hasval(row.highlightColor) ? row.highlightColor : parseCssColor('white');
+ this._highlightWidth = row.highlightWidth;
+ this._highlightInsets = row.highlightInsets;
+ this._attrsChanged.fire();
+ }
+
+ get cursorGuid(): string {
+ return this._cursorGuid;
+ }
+
+ set cursorGuid(cursorGuid: string) {
+ this._cursorGuid = cursorGuid;
+ this._attrsChanged.fire();
+ }
+
+ get rowHeight(): number {
+ return this._rowHeight;
+ }
+
+ set rowHeight(rowHeight: number) {
+ this._rowHeight = rowHeight;
+ this._attrsChanged.fire();
+ }
+
+ get hidden(): boolean {
+ return this._hidden;
+ }
+
+ set hidden(hidden: boolean) {
+ this._hidden = hidden;
+ this._attrsChanged.fire();
+ }
+
+ get dataAxis(): Axis1D {
+ return this._dataAxis;
+ }
+
+ set dataAxis(dataAxis: Axis1D) {
+ this._dataAxis = dataAxis;
+ this._attrsChanged.fire();
+ }
+
+ get label(): string {
+ return this._label;
+ }
+
+ set label(label: string) {
+ if (label !== this._label) {
+ this._label = label;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get truncate(): boolean {
+ return this._truncate;
+ }
+
+ set truncate(truncate: boolean) {
+ if (truncate !== this._truncate) {
+ this._truncate = truncate;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get uiHint(): string {
+ return this._uiHint;
+ }
+
+ set uiHint(uiHint: string) {
+ if (uiHint !== this._uiHint) {
+ this._uiHint = uiHint;
+ this._attrsChanged.fire();
}
+ }
+
+ get bgColor(): Color {
+ return this._bgColor;
+ }
+
+ set bgColor(bgColor: Color) {
+ if (bgColor !== this._bgColor) {
+ this._bgColor = bgColor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get bgLabelColor(): Color {
+ return this._bgLabelColor;
+ }
+
+ set bgLabelColor(bgLabelColor: Color) {
+ if (bgLabelColor !== this._bgLabelColor) {
+ this._bgLabelColor = bgLabelColor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get fgLabelColor(): Color {
+ return this._fgLabelColor;
+ }
+
+ set fgLabelColor(fgLabelColor: Color) {
+ if (fgLabelColor !== this._fgLabelColor) {
+ this._fgLabelColor = fgLabelColor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get labelFont(): string {
+ return this._labelFont;
+ }
+
+ set labelFont(labelFont: string) {
+ if (labelFont !== this._labelFont) {
+ this._labelFont = labelFont;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get cursor(): string {
+ return this._cursor;
+ }
+
+ set cursor(cursor: string) {
+ if (cursor !== this._cursor) {
+ this._cursor = cursor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get highlighted(): boolean {
+ return this._highlighted;
+ }
+
+ set highlighted(highlighted: boolean) {
+ if (highlighted !== this._highlighted) {
+ this._highlighted = highlighted;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get highlightColor(): Color {
+ return this._highlightColor;
+ }
+
+ set highlightColor(highlightColor: Color) {
+ if (highlightColor !== this._highlightColor) {
+ this._highlightColor = highlightColor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get highlightWidth(): number {
+ return this._highlightWidth;
+ }
+
+ set highlightWidth(highlightWidth: number) {
+ if (highlightWidth !== this._highlightWidth) {
+ this._highlightWidth = highlightWidth;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get highlightInsets(): Insets {
+ return this._highlightInsets;
+ }
+
+ set highlightInsets(highlightInsets: Insets) {
+ if (highlightInsets !== this._highlightInsets) {
+ this._highlightInsets = highlightInsets;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get eventGuids(): OrderedStringSet {
+ return this._eventGuids;
+ }
+
+ get timeseriesGuids(): OrderedStringSet {
+ return this._timeseriesGuids;
+ }
+
+ get annotationGuids(): OrderedStringSet {
+ return this._annotationGuids;
+ }
+
+ snapshot(): TimelineRow {
+ return {
+ rowGuid: this._rowGuid,
+ label: this._label,
+ truncate: this._truncate,
+ rowHeight: this._rowHeight,
+ hidden: this._hidden,
+ uiHint: this._uiHint,
+ eventGuids: this._eventGuids.toArray(),
+ timeseriesGuids: this._timeseriesGuids.toArray(),
+ annotationGuids: this._annotationGuids.toArray(),
+ cursorGuid: this._cursorGuid,
+ bgColor: (hasval(this._bgColor) ? this._bgColor.cssString : null),
+ bgLabelColor: (hasval(this._bgLabelColor) ? this._bgLabelColor.cssString : null),
+ fgLabelColor: (hasval(this._fgLabelColor) ? this._fgLabelColor.cssString : null),
+ labelFont: this._labelFont,
+ cursor: this._cursor,
+ highlighted: (hasval(this._highlighted) ? this._highlighted : false),
+ highlightColor: hasval(this.highlightColor) ? this.highlightColor : parseCssColor('white'),
+ highlightWidth: this._highlightWidth,
+ highlightInsets: this._highlightInsets
+ };
+ }
+}
+
+
+export class TimelineGroupModel {
+ private _groupGuid: string;
+ private _rollupGuid: string;
+ private _attrsChanged: Notification;
+ private _hidden: boolean;
+ private _label: string;
+ private _collapsed: boolean;
+ private _highlighted: boolean;
+ private _highlightColor: Color;
+ private _highlightWidth: number;
+ private _highlightInsets: Insets;
+ private _dashPattern: number;
+ private _dashLength: number;
+ private _labelFont: string;
+ private _rowGuids: OrderedStringSet;
+ private _cursor: string;
+
+ constructor(group: TimelineGroup) {
+ this._groupGuid = group.groupGuid;
+ this._attrsChanged = new Notification();
+ this.setAttrs(group);
+ this._rowGuids = new OrderedStringSet(group.rowGuids);
+ }
+
+ get groupGuid(): string {
+ return this._groupGuid;
+ }
+
+ get rollupGuid(): string {
+ return this._rollupGuid;
+ }
+
+ set rollupGuid(rollupGuid: string) {
+ this._rollupGuid = rollupGuid;
+ this._attrsChanged.fire();
+ }
+
+ get attrsChanged(): Notification {
+ return this._attrsChanged;
+ }
+
+ setAttrs(group: TimelineGroup) {
+ // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
+ this._rollupGuid = group.rollupGuid;
+ this._hidden = group.hidden;
+ this._label = group.label;
+ this._collapsed = group.collapsed;
+ this._highlighted = hasval(group.highlighted) ? group.highlighted : false;
+ this._highlightColor = hasval(group.highlightColor) ? group.highlightColor : parseCssColor('white');
+ this._dashPattern = hasval(group.dashPattern) ? group.dashPattern : 0xFFFF;
+ this._dashLength = hasval(group.dashLength) ? group.dashLength : 16;
+ this._highlightWidth = group.highlightWidth;
+ this._highlightInsets = group.highlightInsets;
+ this._labelFont = group.labelFont;
+ this._cursor = group.cursor;
+ this._attrsChanged.fire();
+ }
+
+ get hidden(): boolean {
+ return this._hidden;
+ }
+
+ set hidden(hidden: boolean) {
+ this._hidden = hidden;
+ this._attrsChanged.fire();
+ }
+
+ get label(): string {
+ return this._label;
+ }
+
+ set label(label: string) {
+ if (label !== this._label) {
+ this._label = label;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get collapsed(): boolean {
+ return this._collapsed;
+ }
+
+ set collapsed(collapsed: boolean) {
+ if (collapsed !== this._collapsed) {
+ this._collapsed = collapsed;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get highlighted(): boolean {
+ return this._highlighted;
+ }
+
+ set highlighted(highlighted: boolean) {
+ if (highlighted !== this._highlighted) {
+ this._highlighted = highlighted;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get highlightColor(): Color {
+ return this._highlightColor;
+ }
+
+ set highlightColor(highlightColor: Color) {
+ if (highlightColor !== this._highlightColor) {
+ this._highlightColor = highlightColor;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get highlightWidth(): number {
+ return this._highlightWidth;
+ }
+
+ set highlightWidth(highlightWidth: number) {
+ if (highlightWidth !== this._highlightWidth) {
+ this._highlightWidth = highlightWidth;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get highlightInsets(): Insets {
+ return this._highlightInsets;
+ }
+
+ set highlightInsets(highlightInsets: Insets) {
+ if (highlightInsets !== this._highlightInsets) {
+ this._highlightInsets = highlightInsets;
+ this._attrsChanged.fire();
+ }
+ }
+
+ get dashPattern(): number {
+ return this._dashPattern;
+ }
- get lineThickness( ) : number {
- return this._lineThickness;
+ set dashPattern(dashPattern: number) {
+ if (dashPattern !== this._dashPattern) {
+ this._dashPattern = dashPattern;
+ this._attrsChanged.fire();
}
+ }
- set lineThickness( lineThickness : number ) {
- if ( lineThickness !== this._lineThickness ) {
- this._lineThickness = lineThickness;
- this._attrsChanged.fire( );
- }
- }
+ get dashLength(): number {
+ return this._dashLength;
+ }
- get pointSize( ) : number {
- return this._pointSize;
+ set dashLength(dashLength: number) {
+ if (dashLength !== this._dashLength) {
+ this._dashLength = dashLength;
+ this._attrsChanged.fire();
}
+ }
- set pointSize( pointSize : number ) {
- if ( pointSize !== this._pointSize ) {
- this._pointSize = pointSize;
- this._attrsChanged.fire( );
- }
- }
+ get labelFont(): string {
+ return this._labelFont;
+ }
- get uiHint( ) : string {
- return this._uiHint;
+ set labelFont(labelFont: string) {
+ if (labelFont !== this._labelFont) {
+ this._labelFont = labelFont;
+ this._attrsChanged.fire();
}
+ }
- set uiHint( uiHint : string ) {
- if ( uiHint !== this._uiHint ) {
- this._uiHint = uiHint;
- this._attrsChanged.fire( );
- }
- }
-
- get fragmentGuids( ) : OrderedStringSet {
- return this._fragmentGuids;
- }
-
- set fragmentGuids( fragmentGuids : OrderedStringSet ) {
- if ( fragmentGuids !== this._fragmentGuids ) {
- this._fragmentGuids = fragmentGuids;
- this._attrsChanged.fire( );
- }
- }
+ get cursor(): string {
+ return this._cursor;
+ }
- snapshot( ) : TimelineTimeseries {
- return {
- timeseriesGuid: this._timeseriesGuid,
- uiHint: this._uiHint,
- baseline: this._baseline,
- lineColor: ( hasval( this._lineColor ) ? this._lineColor.cssString : null ),
- pointColor: ( hasval( this._pointColor ) ? this._pointColor.cssString : null ),
- lineThickness: this._lineThickness,
- pointSize: this._pointSize,
- fragmentGuids: this._fragmentGuids.toArray( ),
- };
+ set cursor(cursor: string) {
+ if (cursor !== this._cursor) {
+ this._cursor = cursor;
+ this._attrsChanged.fire();
}
}
+ get rowGuids(): OrderedStringSet {
+ return this._rowGuids;
+ }
- export class TimelineTimeseriesFragmentModel {
- private _fragmentGuid : string;
- // notification provides the start and end indexes of the modified range
- // start index is inclusive, end index is exclusive
- private _dataChanged : Notification2;
- private _attrsChanged : Notification;
- private _userEditMode : string;
- private _data : number[];
- private _times_PMILLIS : number[];
+ snapshot(): TimelineGroup {
+ return {
+ groupGuid: this._groupGuid,
+ rollupGuid: this._rollupGuid,
+ label: this._label,
+ hidden: this._hidden,
+ collapsed: (hasval(this._collapsed) ? this._collapsed : false),
+ highlighted: (hasval(this._highlighted) ? this._highlighted : false),
+ highlightColor: hasval(this.highlightColor) ? this.highlightColor : parseCssColor('white'),
+ highlightWidth: this._highlightWidth,
+ highlightInsets: this._highlightInsets,
+ dashPattern: hasval(this.dashPattern) ? this.dashPattern : 0xFFFF,
+ dashLength: hasval(this.dashLength) ? this.dashLength : 16,
+ labelFont: this._labelFont,
+ cursor: this._cursor,
+ rowGuids: this._rowGuids.toArray()
+ };
+ }
+}
+
+
+export class TimelineRootModel {
+ private _attrsChanged: Notification;
+ private _groupGuids: OrderedStringSet;
+ private _topPinnedRowGuids: OrderedStringSet;
+ private _bottomPinnedRowGuids: OrderedStringSet;
+ private _maximizedRowGuids: OrderedStringSet;
+
+ constructor(root: TimelineRoot) {
+ this._attrsChanged = new Notification();
+ this.setAttrs(root);
+ this._groupGuids = new OrderedStringSet(root.groupGuids);
+ this._topPinnedRowGuids = new OrderedStringSet(root.topPinnedRowGuids || []);
+ this._bottomPinnedRowGuids = new OrderedStringSet(root.bottomPinnedRowGuids || []);
+ this._maximizedRowGuids = new OrderedStringSet(root.maximizedRowGuids || []);
+ }
- constructor( fragment : TimelineTimeseriesFragment ) {
- this._fragmentGuid = fragment.fragmentGuid;
- this._attrsChanged = new Notification( );
- this._dataChanged = new Notification2( );
- this.setAttrs( fragment );
- }
+ get attrsChanged(): Notification {
+ return this._attrsChanged;
+ }
- get fragmentGuid( ) : string {
- return this._fragmentGuid;
- }
+ setAttrs(root: TimelineRoot) {
+ // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
+ // No attrs yet
+ this._attrsChanged.fire();
+ }
- get dataChanged( ) : Notification2 {
- return this._dataChanged;
- }
+ get groupGuids(): OrderedStringSet {
+ return this._groupGuids;
+ }
- setAttrs( fragment : TimelineTimeseriesFragment ) {
- this._userEditMode = fragment.userEditMode;
- this._times_PMILLIS = hasval( fragment.times_ISO8601 ) ? fragment.times_ISO8601.map( parseTime_PMILLIS ) : [];
- this._data = hasval( fragment.data ) ? fragment.data.slice( ) : [];
- this._dataChanged.fire( 0, this._data.length );
- this._attrsChanged.fire( );
- }
+ get topPinnedRowGuids(): OrderedStringSet {
+ return this._topPinnedRowGuids;
+ }
- get data( ) : number[] {
- return this._data;
- }
+ get bottomPinnedRowGuids(): OrderedStringSet {
+ return this._bottomPinnedRowGuids;
+ }
- set data( data : number[] ) {
- if ( data !== this._data ) {
- this._data = data;
- this._dataChanged.fire( 0, this._data.length );
- }
- }
+ get maximizedRowGuids(): OrderedStringSet {
+ return this._maximizedRowGuids;
+ }
- get times_PMILLIS( ) : number[] {
- return this._times_PMILLIS;
- }
+ snapshot(): TimelineRoot {
+ return {
+ groupGuids: this._groupGuids.toArray(),
+ topPinnedRowGuids: this._topPinnedRowGuids.toArray(),
+ bottomPinnedRowGuids: this._bottomPinnedRowGuids.toArray(),
+ maximizedRowGuids: this._maximizedRowGuids.toArray()
+ };
+ }
+}
- // Time should only be modified in a way which keeps the _times_PMILLIS
- // array sorted. This is currently not enforced by the model.
- set times_PMILLIS( times_PMILLIS : number[] ) {
- if ( times_PMILLIS !== this._times_PMILLIS ) {
- this._times_PMILLIS = times_PMILLIS;
- this._dataChanged.fire( 0, this._data.length );
- }
- }
-
- // Time should only be modified in a way which keeps the _times_PMILLIS
- // array sorted. This is currently not enforced by the model.
- setAllData( data : number[], times_PMILLIS : number[] ) {
- if ( data !== this._data || times_PMILLIS !== this._times_PMILLIS ) {
- this._data = data;
- this._times_PMILLIS = times_PMILLIS;
- this._dataChanged.fire( 0, this._data.length );
- }
- }
-
- // Handles adjusting the _times_PMILLIS and _data arrays if the new time
- // requires them to be rearranged to stay in time order. Returns the new
- // index assigned to the data point.
- setData( index : number, value : number, time? : number ) : number {
- if ( this._data[index] !== value || ( hasval( time ) && this._times_PMILLIS[index] !== time ) ) {
- if ( hasval( time ) ) {
- // the new time value would maintain the sorted order of the array
- if ( ( index === 0 || time > this._times_PMILLIS[index-1] ) &&
- ( index === this._times_PMILLIS.length-1 || time < this._times_PMILLIS[index+1] ) ) {
- this._times_PMILLIS[index] = time;
- this._data[index] = value;
- this._dataChanged.fire( index, index+1 );
- }
- else {
- // remove the current point at index
- this._times_PMILLIS.splice( index, 1 );
- this._data.splice( index, 1 );
-
- // find the index to reinsert new data at
- index = indexOf( this._times_PMILLIS, time );
- if ( index < 0 ) index = -index-1;
-
- this._times_PMILLIS.splice( index, 0, time );
- this._data.splice( index, 0, value );
- this._dataChanged.fire( index, index+1 );
- }
- }
- else {
- this._data[index] = value;
- this._dataChanged.fire( index, index+1 );
- }
- }
-
- return index;
- }
-
- get start_PMILLIS( ) : number {
- return this._times_PMILLIS[ 0 ];
- }
- get end_PMILLIS( ) : number {
- return this._times_PMILLIS.slice( -1 )[ 0 ];
- }
-
- get userEditMode( ) : string {
- return this._userEditMode;
- }
+export interface TimelineMergeStrategy {
+ updateCursorModel(cursorModel: TimelineCursorModel, newCursor: TimelineCursor): void;
+ updateAnnotationModel(annotationModel: TimelineAnnotationModel, newAnnotation: TimelineAnnotation): void;
+ updateTimeseriesFragmentModel(timeseriesFragmentModel: TimelineTimeseriesFragmentModel, newTimeseriesFragment: TimelineTimeseriesFragment): void;
+ updateTimeseriesModel(timeseriesModel: TimelineTimeseriesModel, newTimeseries: TimelineTimeseries): void;
+ updateEventModel(eventModel: TimelineEventModel, newEvent: TimelineEvent): void;
+ updateRowModel(rowModel: TimelineRowModel, newRow: TimelineRow): void;
+ updateGroupModel(groupModel: TimelineGroupModel, newGroup: TimelineGroup): void;
+ updateRootModel(rootModel: TimelineRootModel, newRoot: TimelineRoot): void;
+}
- set userEditMode( userEditMode : string ) {
- if ( userEditMode !== this._userEditMode ) {
- this._userEditMode = userEditMode;
- this._attrsChanged.fire( );
- }
- }
- snapshot( ) : TimelineTimeseriesFragment {
- return {
- userEditMode: this._userEditMode,
- fragmentGuid: this._fragmentGuid,
- data: this._data.slice( ),
- times_ISO8601: this._times_PMILLIS.map( formatTime_ISO8601 )
- };
- }
- }
-
-
- export class TimelineEventModel {
- private _eventGuid : string;
- private _attrsChanged : Notification;
- private _startLimit_PMILLIS : number;
- private _endLimit_PMILLIS : number;
- private _start_PMILLIS : number;
- private _end_PMILLIS : number;
- private _label : string;
- private _labelIcon : string;
- private _userEditable : boolean;
- private _styleGuid : string;
- private _order : number;
- private _topMargin : number;
- private _bottomMargin : number;
- private _fgColor : Color;
- private _bgColor : Color;
- private _bgSecondaryColor : Color;
- private _borderColor : Color;
- private _borderSecondaryColor : Color;
- private _labelTopMargin : number;
- private _labelBottomMargin : number;
- private _labelVAlign : number;
- private _labelVPos : number;
- private _labelHAlign : number;
- private _labelHPos : number;
- private _isBorderDashed : boolean;
- private _fillPattern: FillPattern;
-
- constructor( event : TimelineEvent ) {
- this._eventGuid = event.eventGuid;
- this._attrsChanged = new Notification( );
- this.setAttrs( event );
- }
-
- get eventGuid( ) : string {
- return this._eventGuid;
- }
-
- get attrsChanged( ) : Notification {
- return this._attrsChanged;
- }
-
- setAttrs( event : TimelineEvent ) {
- // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
- this._startLimit_PMILLIS = ( hasval( event.startLimit_ISO8601 ) ? parseTime_PMILLIS( event.startLimit_ISO8601 ) : null );
- this._endLimit_PMILLIS = ( hasval( event.endLimit_ISO8601 ) ? parseTime_PMILLIS( event.endLimit_ISO8601 ) : null );
- this._start_PMILLIS = parseTime_PMILLIS( event.start_ISO8601 );
- this._end_PMILLIS = parseTime_PMILLIS( event.end_ISO8601 );
- this._label = event.label;
- this._labelIcon = event.labelIcon;
- this._userEditable = ( hasval( event.userEditable ) ? event.userEditable : false );
- this._styleGuid = event.styleGuid;
- this._order = event.order;
- this._topMargin = event.topMargin;
- this._bottomMargin = event.bottomMargin;
- this._fgColor = ( hasval( event.fgColor ) ? parseCssColor( event.fgColor ) : null );
- this._bgColor = ( hasval( event.bgColor ) ? parseCssColor( event.bgColor ) : null );
- this._bgSecondaryColor = ( hasval( event.bgSecondaryColor ) ? parseCssColor( event.bgSecondaryColor ) : null );
- this._borderColor = ( hasval( event.borderColor ) ? parseCssColor( event.borderColor ) : null );
- this._borderSecondaryColor = ( hasval( event.borderSecondaryColor ) ? parseCssColor( event.borderSecondaryColor ) : null );
- this._labelTopMargin = event.labelTopMargin;
- this._labelBottomMargin = event.labelBottomMargin;
- this._labelVAlign = event.labelVAlign;
- this._labelVPos = event.labelVPos;
- this._labelHAlign = event.labelHAlign;
- this._labelHPos = event.labelHPos;
- this._isBorderDashed = ( hasval( event.isBorderDashed ) ? event.isBorderDashed : false );
- this._fillPattern = ( hasval( event.fillPattern ) ? FillPattern[ event.fillPattern ] : FillPattern.solid );
- this._attrsChanged.fire( );
- }
-
- setInterval( start_PMILLIS : number, end_PMILLIS : number ) {
- if ( start_PMILLIS !== this._start_PMILLIS || end_PMILLIS !== this._end_PMILLIS ) {
-
- var initial_start_PMILLIS = this._start_PMILLIS;
- var initial_end_PMILLIS = this._end_PMILLIS;
-
- var underStartLimit = hasval( this._startLimit_PMILLIS ) && start_PMILLIS < this._startLimit_PMILLIS;
- var overEndLimit = hasval( this._endLimit_PMILLIS ) && end_PMILLIS > this._endLimit_PMILLIS;
- var duration_PMILLIS = end_PMILLIS - start_PMILLIS;
- var durationLimit_PMILLIS = this._endLimit_PMILLIS - this._startLimit_PMILLIS;
-
- // If both limits are present and the event is larger than the total distance between them
- // then shrink the event to fit between the limits.
- if ( hasval( this._startLimit_PMILLIS ) && hasval( this._endLimit_PMILLIS ) && durationLimit_PMILLIS < duration_PMILLIS ) {
- this._start_PMILLIS = this._startLimit_PMILLIS;
- this._end_PMILLIS = this._endLimit_PMILLIS;
- }
- // Otherwise shift the event to comply with the limits without adjusting its total duration
- else if ( underStartLimit ) {
- this._start_PMILLIS = this._startLimit_PMILLIS;
- this._end_PMILLIS = this._start_PMILLIS + duration_PMILLIS;
- }
- else if ( overEndLimit ) {
- this._end_PMILLIS = this._endLimit_PMILLIS;
- this._start_PMILLIS = this._end_PMILLIS - duration_PMILLIS;
- }
- else {
- this._end_PMILLIS = end_PMILLIS;
- this._start_PMILLIS = start_PMILLIS;
- }
-
- // its possible due to the limits that the values didn't actually change
- // only fire attrsChanged if one of the values did actually change
- if ( initial_start_PMILLIS !== this._start_PMILLIS || initial_end_PMILLIS !== this._end_PMILLIS ) {
- this._attrsChanged.fire( );
- }
- }
- }
-
- private limit_start_PMILLIS( start_PMILLIS : number ) {
- return hasval( this._startLimit_PMILLIS ) ? Math.max( start_PMILLIS, this._startLimit_PMILLIS ) : start_PMILLIS;
- }
-
- private limit_end_PMILLIS( end_PMILLIS : number ) {
- return hasval( this._endLimit_PMILLIS ) ? Math.min( end_PMILLIS, this._endLimit_PMILLIS ) : end_PMILLIS;
- }
-
- get start_PMILLIS( ) : number {
- return this._start_PMILLIS;
- }
+export class TimelineModel {
+ private _cursors: OrderedSet;
+ private _annotations: OrderedSet;
+ private _timeseriesFragments: OrderedSet;
+ private _timeseries: OrderedSet;
+ private _events: OrderedSet;
+ private _rows: OrderedSet;
+ private _groups: OrderedSet;
+ private _root: TimelineRootModel;
- set start_PMILLIS( start_PMILLIS : number ) {
- if ( start_PMILLIS !== this._start_PMILLIS ) {
- this._start_PMILLIS = this.limit_start_PMILLIS( start_PMILLIS );
- this._attrsChanged.fire( );
- }
- }
+ constructor(timeline?: Timeline) {
- get end_PMILLIS( ) : number {
- return this._end_PMILLIS;
+ const cursors = (hasval(timeline) && hasval(timeline.cursors) ? timeline.cursors : []);
+ this._cursors = new OrderedSet([], (g) => g.cursorGuid);
+ for (let n = 0; n < cursors.length; n++) {
+ this._cursors.add(new TimelineCursorModel(cursors[n]));
}
- set end_PMILLIS( end_PMILLIS : number ) {
- if ( end_PMILLIS !== this._end_PMILLIS ) {
- this._end_PMILLIS = this.limit_end_PMILLIS( end_PMILLIS );
- this._attrsChanged.fire( );
- }
- }
-
- get startLimit_PMILLIS( ) : number {
- return this._startLimit_PMILLIS;
+ const annotations = (hasval(timeline) && hasval(timeline.annotations) ? timeline.annotations : []);
+ this._annotations = new OrderedSet([], (g) => g.annotationGuid);
+ for (let n = 0; n < annotations.length; n++) {
+ this._annotations.add(new TimelineAnnotationModel(annotations[n]));
}
- set startLimit_PMILLIS( startLimit_PMILLIS : number ) {
- if ( startLimit_PMILLIS !== this._startLimit_PMILLIS ) {
- this._startLimit_PMILLIS = startLimit_PMILLIS;
- this._start_PMILLIS = this.limit_start_PMILLIS( this._start_PMILLIS );
- this._attrsChanged.fire( );
- }
+ const timeseriesFragments = (hasval(timeline) && hasval(timeline.timeseriesFragments) ? timeline.timeseriesFragments : []);
+ this._timeseriesFragments = new OrderedSet([], (e) => e.fragmentGuid);
+ for (let n = 0; n < timeseriesFragments.length; n++) {
+ this._timeseriesFragments.add(new TimelineTimeseriesFragmentModel(timeseriesFragments[n]));
}
- get endLimit_PMILLIS( ) : number {
- return this._endLimit_PMILLIS;
+ const timeseries = (hasval(timeline) && hasval(timeline.timeseries) ? timeline.timeseries : []);
+ this._timeseries = new OrderedSet([], (e) => e.timeseriesGuid);
+ for (let n = 0; n < timeseries.length; n++) {
+ this._timeseries.add(new TimelineTimeseriesModel(timeseries[n]));
}
- set endLimit_PMILLIS( endLimit_PMILLIS : number ) {
- if ( endLimit_PMILLIS !== this._endLimit_PMILLIS ) {
- this._endLimit_PMILLIS = endLimit_PMILLIS;
- this._end_PMILLIS = this.limit_end_PMILLIS( this._end_PMILLIS );
- this._attrsChanged.fire( );
- }
+ const events = (hasval(timeline) && hasval(timeline.events) ? timeline.events : []);
+ this._events = new OrderedSet([], (e) => e.eventGuid);
+ for (let n = 0; n < events.length; n++) {
+ this._events.add(new TimelineEventModel(events[n]));
}
-
- get label( ) : string {
- return this._label;
+ const rows = (hasval(timeline) && hasval(timeline.rows) ? timeline.rows : []);
+ this._rows = new OrderedSet([], (r) => r.rowGuid);
+ for (let n = 0; n < rows.length; n++) {
+ this._rows.add(new TimelineRowModel(rows[n]));
}
- set label( label : string ) {
- if ( label !== this._label ) {
- this._label = label;
- this._attrsChanged.fire( );
- }
+ const groups = (hasval(timeline) && hasval(timeline.groups) ? timeline.groups : []);
+ this._groups = new OrderedSet([], (g) => g.groupGuid);
+ for (let n = 0; n < groups.length; n++) {
+ this._groups.add(new TimelineGroupModel(groups[n]));
}
- get labelIcon( ) : string {
- return this._labelIcon;
- }
+ const root = (hasval(timeline) && hasval(timeline.root) ? timeline.root : newEmptyTimelineRoot());
+ this._root = new TimelineRootModel(root);
+ }
+
+ get cursors(): OrderedSet { return this._cursors; }
+ get annotations(): OrderedSet { return this._annotations; }
+ get timeseriesFragments(): OrderedSet { return this._timeseriesFragments; }
+ get timeseriesSets(): OrderedSet { return this._timeseries; }
+ get events(): OrderedSet { return this._events; }
+ get rows(): OrderedSet { return this._rows; }
+ get groups(): OrderedSet { return this._groups; }
+ get root(): TimelineRootModel { return this._root; }
+
+ cursor(cursorGuid: string): TimelineCursorModel { return this._cursors.valueFor(cursorGuid); }
+ annotation(annotationGuid: string): TimelineAnnotationModel { return this._annotations.valueFor(annotationGuid); }
+ timeseriesFragment(fragmentGuid: string): TimelineTimeseriesFragmentModel { return this._timeseriesFragments.valueFor(fragmentGuid); }
+ timeseries(timeseriesGuid: string): TimelineTimeseriesModel { return this._timeseries.valueFor(timeseriesGuid); }
+ event(eventGuid: string): TimelineEventModel { return this._events.valueFor(eventGuid); }
+ row(rowGuid: string): TimelineRowModel { return this._rows.valueFor(rowGuid); }
+ group(groupGuid: string): TimelineGroupModel { return this._groups.valueFor(groupGuid); }
- set labelIcon( labelIcon : string ) {
- if ( labelIcon !== this._labelIcon ) {
- this._labelIcon = labelIcon;
- this._attrsChanged.fire( );
- }
- }
- get userEditable( ) : boolean {
- return this._userEditable;
- }
+ replace(newTimeline: Timeline) {
+
+ // Purge removed items
+ //
+
+ const freshRoot = newTimeline.root;
+ this._root.groupGuids.retainValues(freshRoot.groupGuids);
+ this._root.topPinnedRowGuids.retainValues(freshRoot.topPinnedRowGuids);
+ this._root.bottomPinnedRowGuids.retainValues(freshRoot.bottomPinnedRowGuids);
+ this._root.maximizedRowGuids.retainValues(freshRoot.maximizedRowGuids);
- set userEditable( userEditable : boolean ) {
- if ( userEditable !== this._userEditable ) {
- this._userEditable = userEditable;
- this._attrsChanged.fire( );
+ const freshGroups = newTimeline.groups;
+ const retainedGroupGuids: string[] = [];
+ for (let n = 0; n < freshGroups.length; n++) {
+ const freshGroup = freshGroups[n];
+ const groupGuid = freshGroup.groupGuid;
+ const oldGroup = this._groups.valueFor(groupGuid);
+ if (hasval(oldGroup)) {
+ oldGroup.rowGuids.retainValues(freshGroup.rowGuids);
+ retainedGroupGuids.push(groupGuid);
}
}
+ this._groups.retainIds(retainedGroupGuids);
- get styleGuid( ) : string {
- return this._styleGuid;
- }
-
- set styleGuid( styleGuid : string ) {
- if ( styleGuid !== this._styleGuid ) {
- this._styleGuid = styleGuid;
- this._attrsChanged.fire( );
- }
- }
-
- get order( ) : number {
- return this._order;
- }
-
- set order( order : number ) {
- if ( order !== this._order ) {
- this._order = order;
- this._attrsChanged.fire( );
- }
- }
-
- get topMargin( ) : number {
- return this._topMargin;
- }
-
- set topMargin( topMargin : number ) {
- if ( topMargin !== this._topMargin ) {
- this._topMargin = topMargin;
- this._attrsChanged.fire( );
- }
- }
-
- get bottomMargin( ) : number {
- return this._bottomMargin;
- }
-
- set bottomMargin( bottomMargin : number ) {
- if ( bottomMargin !== this._bottomMargin ) {
- this._bottomMargin = bottomMargin;
- this._attrsChanged.fire( );
+ const freshRows = newTimeline.rows;
+ const retainedRowGuids: string[] = [];
+ for (let n = 0; n < freshRows.length; n++) {
+ const freshRow = freshRows[n];
+ const rowGuid = freshRow.rowGuid;
+ const oldRow = this._rows.valueFor(rowGuid);
+ if (hasval(oldRow)) {
+ oldRow.eventGuids.retainValues(freshRow.eventGuids || []);
+ retainedRowGuids.push(rowGuid);
}
}
+ this._rows.retainIds(retainedRowGuids);
- get fgColor( ) : Color {
- return this._fgColor;
- }
-
- set fgColor( fgColor : Color ) {
- if ( fgColor !== this._fgColor ) {
- this._fgColor = fgColor;
- this._attrsChanged.fire( );
+ const freshEvents = newTimeline.events;
+ const retainedEventGuids: string[] = [];
+ for (let n = 0; n < freshEvents.length; n++) {
+ const freshEvent = freshEvents[n];
+ const eventGuid = freshEvent.eventGuid;
+ const oldEvent = this._events.valueFor(eventGuid);
+ if (hasval(oldEvent)) {
+ retainedEventGuids.push(eventGuid);
}
}
+ this._events.retainIds(retainedEventGuids);
- get bgColor( ) : Color {
- return this._bgColor;
- }
-
- set bgColor( bgColor : Color ) {
- if ( bgColor !== this._bgColor ) {
- this._bgColor = bgColor;
- this._attrsChanged.fire( );
+ const freshTimeseriesSet = newTimeline.timeseries;
+ const retainedTimeseriesGuids: string[] = [];
+ for (let n = 0; n < freshTimeseriesSet.length; n++) {
+ const freshTimeseries = freshTimeseriesSet[n];
+ const timeseriesGuid = freshTimeseries.timeseriesGuid;
+ const oldTimeseries = this._timeseries.valueFor(timeseriesGuid);
+ if (hasval(oldTimeseries)) {
+ retainedTimeseriesGuids.push(timeseriesGuid);
}
}
+ this._timeseries.retainIds(retainedTimeseriesGuids);
- get bgSecondaryColor( ) : Color {
- return this._bgSecondaryColor;
- }
-
- set bgSecondaryColor( bgSecondaryColor : Color ) {
- if ( bgSecondaryColor !== this._bgSecondaryColor ) {
- this._bgSecondaryColor = bgSecondaryColor;
- this._attrsChanged.fire( );
+ const freshTimeseriesFragments = newTimeline.timeseriesFragments;
+ const retainedTimeseriesFragmentGuids: string[] = [];
+ for (let n = 0; n < freshTimeseriesFragments.length; n++) {
+ const freshTimeseriesFragment = freshTimeseriesFragments[n];
+ const fragmentGuid = freshTimeseriesFragment.fragmentGuid;
+ const oldTimeseriesFragment = this._timeseriesFragments.valueFor(fragmentGuid);
+ if (hasval(oldTimeseriesFragment)) {
+ retainedTimeseriesFragmentGuids.push(fragmentGuid);
}
}
+ this._timeseriesFragments.retainIds(retainedTimeseriesFragmentGuids);
- get borderColor( ) : Color {
- return this._borderColor;
+ const freshAnnotations = newTimeline.annotations;
+ const retainedAnnotationGuids: string[] = [];
+ for (let n = 0; n < freshAnnotations.length; n++) {
+ const freshAnnotation = freshAnnotations[n];
+ const annotationGuid = freshAnnotation.annotationGuid;
+ const oldAnnotation = this._annotations.valueFor(annotationGuid);
+ if (hasval(oldAnnotation)) {
+ retainedAnnotationGuids.push(annotationGuid);
+ }
}
+ this._annotations.retainIds(retainedAnnotationGuids);
- set borderColor( borderColor : Color ) {
- if ( borderColor !== this._borderColor ) {
- this._borderColor = borderColor;
- this._attrsChanged.fire( );
+ const freshCursors = newTimeline.cursors;
+ const retainedCursorGuids: string[] = [];
+ for (let n = 0; n < freshCursors.length; n++) {
+ const freshCursor = freshCursors[n];
+ const cursorGuid = freshCursor.cursorGuid;
+ const oldCursor = this._cursors.valueFor(cursorGuid);
+ if (hasval(oldCursor)) {
+ retainedCursorGuids.push(cursorGuid);
}
}
+ this._cursors.retainIds(retainedCursorGuids);
- get borderSecondaryColor( ) : Color {
- return this._borderSecondaryColor;
- }
+ // Add new items
+ //
- set borderSecondaryColor( borderSecondaryColor : Color ) {
- if ( borderSecondaryColor !== this._borderSecondaryColor ) {
- this._borderSecondaryColor = borderSecondaryColor;
- this._attrsChanged.fire( );
- }
- }
-
- get labelTopMargin( ) : number {
- return this._labelTopMargin;
- }
-
- set labelTopMargin( labelTopMargin : number ) {
- if ( labelTopMargin !== this._labelTopMargin ) {
- this._labelTopMargin = labelTopMargin;
- this._attrsChanged.fire( );
+ for (let n = 0; n < freshCursors.length; n++) {
+ const freshCursor = freshCursors[n];
+ const oldCursor = this._cursors.valueFor(freshCursor.cursorGuid);
+ if (hasval(oldCursor)) {
+ oldCursor.setAttrs(freshCursor);
}
- }
-
- get labelBottomMargin( ) : number {
- return this._labelBottomMargin;
- }
-
- set labelBottomMargin( labelBottomMargin : number ) {
- if ( labelBottomMargin !== this._labelBottomMargin ) {
- this._labelBottomMargin = labelBottomMargin;
- this._attrsChanged.fire( );
+ else {
+ this._cursors.add(new TimelineCursorModel(freshCursor));
}
}
-
- get labelVAlign( ) : number {
- return this._labelVAlign;
- }
-
- set labelVAlign( labelVAlign : number ) {
- if ( labelVAlign !== this._labelVAlign ) {
- this._labelVAlign = labelVAlign;
- this._attrsChanged.fire( );
+
+ for (let n = 0; n < freshAnnotations.length; n++) {
+ const freshAnnotation = freshAnnotations[n];
+ const oldAnnotation = this._annotations.valueFor(freshAnnotation.annotationGuid);
+ if (hasval(oldAnnotation)) {
+ oldAnnotation.setAttrs(freshAnnotation);
}
- }
-
- get labelVPos( ) : number {
- return this._labelVPos;
- }
-
- set labelVPos( labelVPos : number ) {
- if ( labelVPos !== this._labelVPos ) {
- this._labelVPos = labelVPos;
- this._attrsChanged.fire( );
+ else {
+ this._annotations.add(new TimelineAnnotationModel(freshAnnotation));
}
}
-
- get labelHAlign( ) : number {
- return this._labelHAlign;
- }
-
- set labelHAlign( labelHAlign : number ) {
- if ( labelHAlign !== this._labelHAlign ) {
- this._labelHAlign = labelHAlign;
- this._attrsChanged.fire( );
+
+ for (let n = 0; n < freshTimeseriesFragments.length; n++) {
+ const freshTimeseriesFragment = freshTimeseriesFragments[n];
+ const oldTimeseriesFragment = this._timeseriesFragments.valueFor(freshTimeseriesFragment.fragmentGuid);
+ if (hasval(oldTimeseriesFragment)) {
+ oldTimeseriesFragment.setAttrs(freshTimeseriesFragment);
}
- }
-
- get labelHPos( ) : number {
- return this._labelHPos;
- }
-
- set labelHPos( labelHPos : number ) {
- if ( labelHPos !== this._labelHPos ) {
- this._labelHPos = labelHPos;
- this._attrsChanged.fire( );
+ else {
+ this._timeseriesFragments.add(new TimelineTimeseriesFragmentModel(freshTimeseriesFragment));
}
}
- get isBorderDashed( ) : boolean {
- return this._isBorderDashed;
- }
-
- set isBorderDashed( isBorderDashed : boolean ) {
- if ( isBorderDashed !== this._isBorderDashed ) {
- this._isBorderDashed = isBorderDashed;
- this._attrsChanged.fire( );
+ for (let n = 0; n < freshTimeseriesSet.length; n++) {
+ const freshTimeseries = freshTimeseriesSet[n];
+ const oldTimeseries = this._timeseries.valueFor(freshTimeseries.timeseriesGuid);
+ if (hasval(oldTimeseries)) {
+ oldTimeseries.setAttrs(freshTimeseries);
}
- }
-
- get fillPattern( ) : FillPattern {
- return this._fillPattern;
- }
-
- set fillPattern( fillPattern : FillPattern ) {
- if ( fillPattern !== this._fillPattern ) {
- this._fillPattern = fillPattern;
- this._attrsChanged.fire( );
+ else {
+ this._timeseries.add(new TimelineTimeseriesModel(freshTimeseries));
}
}
- snapshot( ) : TimelineEvent {
- return {
- eventGuid: this._eventGuid,
- startLimit_ISO8601: ( hasval( this._startLimit_PMILLIS ) ? formatTime_ISO8601( this._startLimit_PMILLIS ) : null ),
- endLimit_ISO8601: ( hasval( this._endLimit_PMILLIS ) ? formatTime_ISO8601( this._endLimit_PMILLIS ) : null ),
- start_ISO8601: formatTime_ISO8601( this._start_PMILLIS ),
- end_ISO8601: formatTime_ISO8601( this._end_PMILLIS ),
- label: this._label,
- labelIcon: this._labelIcon,
- userEditable: this._userEditable,
- styleGuid: this._styleGuid,
- order: this._order,
- topMargin: this._topMargin,
- bottomMargin: this._bottomMargin,
- bgColor: ( hasval( this._bgColor ) ? this._bgColor.cssString : null ),
- bgSecondaryColor: ( hasval( this._bgSecondaryColor ) ? this._bgSecondaryColor.cssString : null ),
- fgColor: ( hasval( this._fgColor ) ? this._fgColor.cssString : null ),
- borderColor: ( hasval( this._borderColor ) ? this._borderColor.cssString : null ),
- borderSecondaryColor: ( hasval( this._borderSecondaryColor ) ? this.borderSecondaryColor.cssString : null ),
- labelTopMargin: this._labelTopMargin,
- labelBottomMargin: this._labelBottomMargin,
- labelVAlign: this._labelVAlign,
- labelVPos: this._labelVPos,
- labelHAlign: this._labelHAlign,
- labelHPos: this._labelHPos,
- isBorderDashed: this._isBorderDashed,
- fillPattern: FillPattern[ this._fillPattern ]
- };
- }
- }
-
-
- export class TimelineRowModel {
- private _rowGuid : string;
- private _attrsChanged : Notification;
- private _rowHeight : number;
- private _hidden : boolean;
- private _label : string;
- private _uiHint : string;
- private _eventGuids : OrderedStringSet;
- private _timeseriesGuids : OrderedStringSet;
- private _annotationGuids : OrderedStringSet;
- private _cursorGuid : string;
- private _bgColor : Color;
- private _fgLabelColor : Color;
- private _bgLabelColor : Color;
- private _labelFont : string;
- private _dataAxis : Axis1D;
-
- constructor( row : TimelineRow ) {
- this._rowGuid = row.rowGuid;
- this._attrsChanged = new Notification( );
-
- var min : number = hasval( row.yMin ) ? row.yMin : 0;
- var max : number = hasval( row.yMax ) ? row.yMax : 1;
- this._dataAxis = new Axis1D( min, max );
-
- this.setAttrs( row );
- this._eventGuids = new OrderedStringSet( row.eventGuids || [] );
- this._timeseriesGuids = new OrderedStringSet( row.timeseriesGuids || [] );
- this._annotationGuids = new OrderedStringSet( row.annotationGuids || [] );
- }
-
- get rowGuid( ) : string {
- return this._rowGuid;
- }
-
- get attrsChanged( ) : Notification {
- return this._attrsChanged;
- }
-
- setAttrs( row : TimelineRow ) {
- // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
- this._label = row.label;
- this._uiHint = row.uiHint;
- this._hidden = row.hidden;
- this._rowHeight = row.rowHeight;
- this._cursorGuid = row.cursorGuid;
- this._bgColor = ( hasval( row.bgColor ) ? parseCssColor( row.bgColor ) : null );
- this._fgLabelColor = ( hasval( row.fgLabelColor ) ? parseCssColor( row.fgLabelColor ) : null );
- this._bgLabelColor = ( hasval( row.bgLabelColor ) ? parseCssColor( row.bgLabelColor ) : null );
- this._labelFont = row.labelFont;
- this._attrsChanged.fire( );
- }
-
- get cursorGuid( ) : string {
- return this._cursorGuid;
- }
-
- set cursorGuid( cursorGuid : string ) {
- this._cursorGuid = cursorGuid;
- this._attrsChanged.fire( );
- }
-
- get rowHeight( ) : number {
- return this._rowHeight;
- }
-
- set rowHeight( rowHeight : number ) {
- this._rowHeight = rowHeight;
- this._attrsChanged.fire( );
- }
-
- get hidden( ) : boolean {
- return this._hidden;
- }
-
- set hidden( hidden : boolean ) {
- this._hidden = hidden;
- this._attrsChanged.fire( );
- }
-
- get dataAxis( ) : Axis1D {
- return this._dataAxis;
- }
-
- set dataAxis( dataAxis : Axis1D ) {
- this._dataAxis = dataAxis;
- this._attrsChanged.fire( );
- }
-
- get label( ) : string {
- return this._label;
- }
-
- set label( label : string ) {
- if ( label !== this._label ) {
- this._label = label;
- this._attrsChanged.fire( );
+ for (let n = 0; n < freshEvents.length; n++) {
+ const freshEvent = freshEvents[n];
+ const oldEvent = this._events.valueFor(freshEvent.eventGuid);
+ if (hasval(oldEvent)) {
+ oldEvent.setAttrs(freshEvent);
}
- }
-
- get uiHint( ) : string {
- return this._uiHint;
- }
-
- set uiHint( uiHint : string ) {
- if ( uiHint !== this._uiHint ) {
- this._uiHint = uiHint;
- this._attrsChanged.fire( );
+ else {
+ this._events.add(new TimelineEventModel(freshEvent));
}
}
-
- get bgColor( ) : Color {
- return this._bgColor;
- }
- set bgColor( bgColor : Color ) {
- if ( bgColor !== this._bgColor ) {
- this._bgColor = bgColor;
- this._attrsChanged.fire( );
+ for (let n = 0; n < freshRows.length; n++) {
+ const freshRow = freshRows[n];
+ const oldRow = this._rows.valueFor(freshRow.rowGuid);
+ if (hasval(oldRow)) {
+ oldRow.setAttrs(freshRow);
+ oldRow.eventGuids.addAll((freshRow.eventGuids || []), 0, true);
}
- }
-
- get bgLabelColor( ) : Color {
- return this._bgLabelColor;
- }
-
- set bgLabelColor( bgLabelColor : Color ) {
- if ( bgLabelColor !== this._bgLabelColor ) {
- this._bgLabelColor = bgLabelColor;
- this._attrsChanged.fire( );
+ else {
+ this._rows.add(new TimelineRowModel(freshRow));
}
}
-
- get fgLabelColor( ) : Color {
- return this._fgLabelColor;
- }
- set fgLabelColor( fgLabelColor : Color ) {
- if ( fgLabelColor !== this._fgLabelColor ) {
- this._fgLabelColor = fgLabelColor;
- this._attrsChanged.fire( );
+ for (let n = 0; n < freshGroups.length; n++) {
+ const freshGroup = freshGroups[n];
+ const oldGroup = this._groups.valueFor(freshGroup.groupGuid);
+ if (hasval(oldGroup)) {
+ oldGroup.setAttrs(freshGroup);
+ oldGroup.rowGuids.addAll(freshGroup.rowGuids, 0, true);
}
- }
-
- get labelFont( ) : string {
- return this._labelFont;
- }
-
- set labelFont( labelFont : string ) {
- if ( labelFont !== this._labelFont ) {
- this._labelFont = labelFont;
- this._attrsChanged.fire( );
+ else {
+ this._groups.add(new TimelineGroupModel(freshGroup));
}
}
- get eventGuids( ) : OrderedStringSet {
- return this._eventGuids;
- }
-
- get timeseriesGuids( ) : OrderedStringSet {
- return this._timeseriesGuids;
- }
-
- get annotationGuids( ) : OrderedStringSet {
- return this._annotationGuids;
- }
-
- snapshot( ) : TimelineRow {
- return {
- rowGuid: this._rowGuid,
- label: this._label,
- rowHeight: this._rowHeight,
- hidden: this._hidden,
- uiHint: this._uiHint,
- eventGuids: this._eventGuids.toArray( ),
- timeseriesGuids: this._timeseriesGuids.toArray( ),
- annotationGuids: this._annotationGuids.toArray( ),
- cursorGuid: this._cursorGuid,
- bgColor: ( hasval( this._bgColor ) ? this._bgColor.cssString : null ),
- bgLabelColor: ( hasval( this._bgLabelColor ) ? this._bgLabelColor.cssString : null ),
- fgLabelColor: ( hasval( this._fgLabelColor ) ? this._fgLabelColor.cssString : null ),
- labelFont: this._labelFont
- };
- }
+ this._root.groupGuids.addAll(freshRoot.groupGuids, 0, true);
+ this._root.topPinnedRowGuids.addAll(freshRoot.topPinnedRowGuids, 0, true);
+ this._root.bottomPinnedRowGuids.addAll(freshRoot.bottomPinnedRowGuids, 0, true);
+ this._root.maximizedRowGuids.addAll(freshRoot.maximizedRowGuids, 0, true);
}
- export class TimelineGroupModel {
- private _groupGuid : string;
- private _rollupGuid : string;
- private _attrsChanged : Notification;
- private _hidden : boolean;
- private _label : string;
- private _collapsed : boolean;
- private _rowGuids : OrderedStringSet;
-
- constructor( group : TimelineGroup ) {
- this._groupGuid = group.groupGuid;
- this._attrsChanged = new Notification( );
- this.setAttrs( group );
- this._rowGuids = new OrderedStringSet( group.rowGuids );
- }
-
- get groupGuid( ) : string {
- return this._groupGuid;
- }
-
- get rollupGuid( ) : string {
- return this._rollupGuid;
- }
-
- set rollupGuid( rollupGuid : string ) {
- this._rollupGuid = rollupGuid;
- this._attrsChanged.fire( );
- }
-
- get attrsChanged( ) : Notification {
- return this._attrsChanged;
- }
-
- setAttrs( group : TimelineGroup ) {
- // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
- this._rollupGuid = group.rollupGuid;
- this._hidden = group.hidden;
- this._label = group.label;
- this._collapsed = group.collapsed;
- this._attrsChanged.fire( );
- }
-
- get hidden( ) : boolean {
- return this._hidden;
- }
-
- set hidden( hidden : boolean ) {
- this._hidden = hidden;
- this._attrsChanged.fire( );
- }
-
- get label( ) : string {
- return this._label;
- }
+ merge(newData: Timeline, strategy: TimelineMergeStrategy) {
- set label( label : string ) {
- if ( label !== this._label ) {
- this._label = label;
- this._attrsChanged.fire( );
+ const newCursors = hasval(newData.cursors) ? newData.cursors : [];
+ for (let n = 0; n < newCursors.length; n++) {
+ const newCursor = newCursors[n];
+ const cursorModel = this._cursors.valueFor(newCursor.cursorGuid);
+ if (hasval(cursorModel)) {
+ strategy.updateCursorModel(cursorModel, newCursor);
}
- }
-
- get collapsed( ) : boolean {
- return this._collapsed;
- }
-
- set collapsed( collapsed : boolean ) {
- if ( collapsed !== this._collapsed ) {
- this._collapsed = collapsed;
- this._attrsChanged.fire( );
+ else {
+ this._cursors.add(new TimelineCursorModel(newCursor));
}
}
- get rowGuids( ) : OrderedStringSet {
- return this._rowGuids;
+ const newAnnotations = hasval(newData.annotations) ? newData.annotations : [];
+ for (let n = 0; n < newAnnotations.length; n++) {
+ const newAnnotation = newAnnotations[n];
+ const annotationModel = this._annotations.valueFor(newAnnotation.annotationGuid);
+ if (hasval(annotationModel)) {
+ strategy.updateAnnotationModel(annotationModel, newAnnotation);
+ }
+ else {
+ this._annotations.add(new TimelineAnnotationModel(newAnnotation));
+ }
}
- snapshot( ) : TimelineGroup {
- return {
- groupGuid: this._groupGuid,
- rollupGuid: this._rollupGuid,
- label: this._label,
- hidden: this._hidden,
- collapsed: ( hasval( this._collapsed ) ? this._collapsed : false ),
- rowGuids: this._rowGuids.toArray( )
- };
+ const newTimeseriesFragments = hasval(newData.timeseriesFragments) ? newData.timeseriesFragments : [];
+ for (let n = 0; n < newTimeseriesFragments.length; n++) {
+ const newTimeseriesFragment = newTimeseriesFragments[n];
+ const timeseriesFragmentModel = this._timeseriesFragments.valueFor(newTimeseriesFragment.fragmentGuid);
+ if (hasval(timeseriesFragmentModel)) {
+ strategy.updateTimeseriesFragmentModel(timeseriesFragmentModel, newTimeseriesFragment);
+ }
+ else {
+ this._timeseriesFragments.add(new TimelineTimeseriesFragmentModel(newTimeseriesFragment));
+ }
}
- }
-
- export class TimelineRootModel {
- private _attrsChanged : Notification;
- private _groupGuids : OrderedStringSet;
- private _topPinnedRowGuids : OrderedStringSet;
- private _bottomPinnedRowGuids : OrderedStringSet;
- private _maximizedRowGuids : OrderedStringSet;
-
- constructor( root : TimelineRoot ) {
- this._attrsChanged = new Notification( );
- this.setAttrs( root );
- this._groupGuids = new OrderedStringSet( root.groupGuids );
- this._topPinnedRowGuids = new OrderedStringSet( root.topPinnedRowGuids || [] );
- this._bottomPinnedRowGuids = new OrderedStringSet( root.bottomPinnedRowGuids || [] );
- this._maximizedRowGuids = new OrderedStringSet( root.maximizedRowGuids || [] );
+ const newTimeseriesSet = hasval(newData.timeseries) ? newData.timeseries : [];
+ for (let n = 0; n < newTimeseriesSet.length; n++) {
+ const newTimeseries = newTimeseriesSet[n];
+ const timeseriesModel = this._timeseries.valueFor(newTimeseries.timeseriesGuid);
+ if (hasval(timeseriesModel)) {
+ strategy.updateTimeseriesModel(timeseriesModel, newTimeseries);
+ }
+ else {
+ this._timeseries.add(new TimelineTimeseriesModel(newTimeseries));
+ }
}
- get attrsChanged( ) : Notification {
- return this._attrsChanged;
+ const newEvents = hasval(newData.events) ? newData.events : [];
+ for (let n = 0; n < newEvents.length; n++) {
+ const newEvent = newEvents[n];
+ const eventModel = this._events.valueFor(newEvent.eventGuid);
+ if (hasval(eventModel)) {
+ strategy.updateEventModel(eventModel, newEvent);
+ }
+ else {
+ this._events.add(new TimelineEventModel(newEvent));
+ }
}
- setAttrs( root : TimelineRoot ) {
- // Don't both checking whether values are going to change -- it's not that important, and it would be obnoxious here
- // No attrs yet
- this._attrsChanged.fire( );
+ const newRows = hasval(newData.rows) ? newData.rows : [];
+ for (let n = 0; n < newRows.length; n++) {
+ const newRow = newRows[n];
+ const rowModel = this._rows.valueFor(newRow.rowGuid);
+ if (hasval(rowModel)) {
+ strategy.updateRowModel(rowModel, newRow);
+ }
+ else {
+ this._rows.add(new TimelineRowModel(newRow));
+ }
}
- get groupGuids( ) : OrderedStringSet {
- return this._groupGuids;
- }
-
- get topPinnedRowGuids( ) : OrderedStringSet {
- return this._topPinnedRowGuids;
- }
-
- get bottomPinnedRowGuids( ) : OrderedStringSet {
- return this._bottomPinnedRowGuids;
- }
-
- get maximizedRowGuids( ) : OrderedStringSet {
- return this._maximizedRowGuids;
+ const newGroups = hasval(newData.groups) ? newData.groups : [];
+ for (let n = 0; n < newGroups.length; n++) {
+ const newGroup = newGroups[n];
+ const groupModel = this._groups.valueFor(newGroup.groupGuid);
+ if (hasval(groupModel)) {
+ strategy.updateGroupModel(groupModel, newGroup);
+ }
+ else {
+ this._groups.add(new TimelineGroupModel(newGroup));
+ }
}
- snapshot( ) : TimelineRoot {
- return {
- groupGuids: this._groupGuids.toArray( ),
- topPinnedRowGuids: this._topPinnedRowGuids.toArray( ),
- bottomPinnedRowGuids: this._bottomPinnedRowGuids.toArray( ),
- maximizedRowGuids: this._maximizedRowGuids.toArray( )
- };
- }
+ const newRoot = newData.root;
+ strategy.updateRootModel(this._root, newRoot);
}
-
- export interface TimelineMergeStrategy {
- updateCursorModel( cursorModel : TimelineCursorModel, newCursor : TimelineCursor );
- updateAnnotationModel( annotationModel : TimelineAnnotationModel, newAnnotation : TimelineAnnotation );
- updateTimeseriesFragmentModel( timeseriesFragmentModel : TimelineTimeseriesFragmentModel, newTimeseriesFragment : TimelineTimeseriesFragment );
- updateTimeseriesModel( timeseriesModel : TimelineTimeseriesModel, newTimeseries : TimelineTimeseries );
- updateEventModel( eventModel : TimelineEventModel, newEvent : TimelineEvent );
- updateRowModel( rowModel : TimelineRowModel, newRow : TimelineRow );
- updateGroupModel( groupModel : TimelineGroupModel, newGroup : TimelineGroup );
- updateRootModel( rootModel : TimelineRootModel, newRoot : TimelineRoot );
+ snapshot(): Timeline {
+ return {
+ cursors: this._cursors.map((e) => e.snapshot()),
+ annotations: this._annotations.map((e) => e.snapshot()),
+ timeseriesFragments: this._timeseriesFragments.map((e) => e.snapshot()),
+ timeseries: this._timeseries.map((e) => e.snapshot()),
+ events: this._events.map((e) => e.snapshot()),
+ rows: this._rows.map((r) => r.snapshot()),
+ groups: this._groups.map((g) => g.snapshot()),
+ root: this._root.snapshot()
+ };
}
- export class TimelineModel {
- private _cursors : OrderedSet;
- private _annotations : OrderedSet;
- private _timeseriesFragments : OrderedSet;
- private _timeseries : OrderedSet;
- private _events : OrderedSet;
- private _rows : OrderedSet;
- private _groups : OrderedSet;
- private _root : TimelineRootModel;
-
- constructor( timeline? : Timeline ) {
-
- var cursors = ( hasval( timeline ) && hasval( timeline.cursors ) ? timeline.cursors : [] );
- this._cursors = new OrderedSet( [], (g)=>g.cursorGuid );
- for ( var n = 0; n < cursors.length; n++ ) {
- this._cursors.add( new TimelineCursorModel( cursors[ n ] ) );
- }
-
- var annotations = ( hasval( timeline ) && hasval( timeline.annotations ) ? timeline.annotations : [] );
- this._annotations = new OrderedSet( [], (g)=>g.annotationGuid );
- for ( var n = 0; n < annotations.length; n++ ) {
- this._annotations.add( new TimelineAnnotationModel( annotations[ n ] ) );
- }
-
- var timeseriesFragments = ( hasval( timeline ) && hasval( timeline.timeseriesFragments ) ? timeline.timeseriesFragments : [] );
- this._timeseriesFragments = new OrderedSet( [], (e)=>e.fragmentGuid );
- for ( var n = 0; n < timeseriesFragments.length; n++ ) {
- this._timeseriesFragments.add( new TimelineTimeseriesFragmentModel( timeseriesFragments[ n ] ) );
- }
-
- var timeseries = ( hasval( timeline ) && hasval( timeline.timeseries ) ? timeline.timeseries : [] );
- this._timeseries = new OrderedSet( [], (e)=>e.timeseriesGuid );
- for ( var n = 0; n < timeseries.length; n++ ) {
- this._timeseries.add( new TimelineTimeseriesModel( timeseries[ n ] ) );
- }
-
- var events = ( hasval( timeline ) && hasval( timeline.events ) ? timeline.events : [] );
- this._events = new OrderedSet( [], (e)=>e.eventGuid );
- for ( var n = 0; n < events.length; n++ ) {
- this._events.add( new TimelineEventModel( events[ n ] ) );
- }
-
- var rows = ( hasval( timeline ) && hasval( timeline.rows ) ? timeline.rows : [] );
- this._rows = new OrderedSet( [], (r)=>r.rowGuid );
- for ( var n = 0; n < rows.length; n++ ) {
- this._rows.add( new TimelineRowModel( rows[ n ] ) );
- }
-
- var groups = ( hasval( timeline ) && hasval( timeline.groups ) ? timeline.groups : [] );
- this._groups = new OrderedSet( [], (g)=>g.groupGuid );
- for ( var n = 0; n < groups.length; n++ ) {
- this._groups.add( new TimelineGroupModel( groups[ n ] ) );
- }
-
- var root = ( hasval( timeline ) && hasval( timeline.root ) ? timeline.root : newEmptyTimelineRoot( ) );
- this._root = new TimelineRootModel( root );
- }
-
- get cursors( ) : OrderedSet { return this._cursors; }
- get annotations( ) : OrderedSet { return this._annotations; }
- get timeseriesFragments( ) : OrderedSet { return this._timeseriesFragments; }
- get timeseriesSets( ) : OrderedSet { return this._timeseries; }
- get events( ) : OrderedSet { return this._events; }
- get rows( ) : OrderedSet { return this._rows; }
- get groups( ) : OrderedSet { return this._groups; }
- get root( ) : TimelineRootModel { return this._root; }
-
- cursor( cursorGuid : string ) : TimelineCursorModel { return this._cursors.valueFor( cursorGuid ); }
- annotation( annotationGuid : string ) : TimelineAnnotationModel { return this._annotations.valueFor( annotationGuid ); }
- timeseriesFragment( fragmentGuid : string ) : TimelineTimeseriesFragmentModel { return this._timeseriesFragments.valueFor( fragmentGuid ); }
- timeseries( timeseriesGuid : string ) : TimelineTimeseriesModel { return this._timeseries.valueFor( timeseriesGuid ); }
- event( eventGuid : string ) : TimelineEventModel { return this._events.valueFor( eventGuid ); }
- row( rowGuid : string ) : TimelineRowModel { return this._rows.valueFor( rowGuid ); }
- group( groupGuid : string ) : TimelineGroupModel { return this._groups.valueFor( groupGuid ); }
-
-
- replace( newTimeline : Timeline ) {
-
- // Purge removed items
- //
-
- var freshRoot = newTimeline.root;
- this._root.groupGuids.retainValues( freshRoot.groupGuids );
- this._root.topPinnedRowGuids.retainValues( freshRoot.topPinnedRowGuids );
- this._root.bottomPinnedRowGuids.retainValues( freshRoot.bottomPinnedRowGuids );
- this._root.maximizedRowGuids.retainValues( freshRoot.maximizedRowGuids );
-
- var freshGroups = newTimeline.groups;
- var retainedGroupGuids : string[] = [];
- for ( var n = 0; n < freshGroups.length; n++ ) {
- var freshGroup = freshGroups[ n ];
- var groupGuid = freshGroup.groupGuid;
- var oldGroup = this._groups.valueFor( groupGuid );
- if ( hasval( oldGroup ) ) {
- oldGroup.rowGuids.retainValues( freshGroup.rowGuids );
- retainedGroupGuids.push( groupGuid );
- }
- }
- this._groups.retainIds( retainedGroupGuids );
-
- var freshRows = newTimeline.rows;
- var retainedRowGuids : string[] = [];
- for ( var n = 0; n < freshRows.length; n++ ) {
- var freshRow = freshRows[ n ];
- var rowGuid = freshRow.rowGuid;
- var oldRow = this._rows.valueFor( rowGuid );
- if ( hasval( oldRow ) ) {
- oldRow.eventGuids.retainValues( freshRow.eventGuids || [] );
- retainedRowGuids.push( rowGuid );
- }
- }
- this._rows.retainIds( retainedRowGuids );
-
- var freshEvents = newTimeline.events;
- var retainedEventGuids : string[] = [];
- for ( var n = 0; n < freshEvents.length; n++ ) {
- var freshEvent = freshEvents[ n ];
- var eventGuid = freshEvent.eventGuid;
- var oldEvent = this._events.valueFor( eventGuid );
- if ( hasval( oldEvent ) ) {
- retainedEventGuids.push( eventGuid );
- }
- }
- this._events.retainIds( retainedEventGuids );
-
- var freshTimeseriesSet = newTimeline.timeseries;
- var retainedTimeseriesGuids : string[] = [];
- for ( var n = 0; n < freshTimeseriesSet.length; n++ ) {
- var freshTimeseries = freshTimeseriesSet[ n ];
- var timeseriesGuid = freshTimeseries.timeseriesGuid;
- var oldTimeseries = this._timeseries.valueFor( timeseriesGuid );
- if ( hasval( oldTimeseries ) ) {
- retainedTimeseriesGuids.push( timeseriesGuid );
- }
- }
- this._timeseries.retainIds( retainedTimeseriesGuids );
-
- var freshTimeseriesFragments = newTimeline.timeseriesFragments;
- var retainedTimeseriesFragmentGuids : string[] = [];
- for ( var n = 0; n < freshTimeseriesFragments.length; n++ ) {
- var freshTimeseriesFragment = freshTimeseriesFragments[ n ];
- var fragmentGuid = freshTimeseriesFragment.fragmentGuid;
- var oldTimeseriesFragment = this._timeseriesFragments.valueFor( fragmentGuid );
- if ( hasval( oldTimeseriesFragment ) ) {
- retainedTimeseriesFragmentGuids.push( fragmentGuid );
- }
- }
- this._timeseriesFragments.retainIds( retainedTimeseriesFragmentGuids );
-
- var freshAnnotations = newTimeline.annotations;
- var retainedAnnotationGuids : string[] = [];
- for ( var n = 0; n < freshAnnotations.length; n++ ) {
- var freshAnnotation = freshAnnotations[ n ];
- var annotationGuid = freshAnnotation.annotationGuid;
- var oldAnnotation = this._annotations.valueFor( annotationGuid );
- if ( hasval( oldAnnotation ) ) {
- retainedAnnotationGuids.push( annotationGuid );
- }
- }
- this._annotations.retainIds( retainedAnnotationGuids );
-
- var freshCursors = newTimeline.cursors;
- var retainedCursorGuids : string[] = [];
- for ( var n = 0; n < freshCursors.length; n++ ) {
- var freshCursor = freshCursors[ n ];
- var cursorGuid = freshCursor.cursorGuid;
- var oldCursor = this._cursors.valueFor( cursorGuid );
- if ( hasval( oldCursor ) ) {
- retainedCursorGuids.push( cursorGuid );
- }
- }
- this._cursors.retainIds( retainedCursorGuids );
-
- // Add new items
- //
-
- for ( var n = 0; n < freshCursors.length; n++ ) {
- var freshCursor = freshCursors[ n ];
- var oldCursor = this._cursors.valueFor( freshCursor.cursorGuid );
- if ( hasval( oldCursor ) ) {
- oldCursor.setAttrs( freshCursor );
- }
- else {
- this._cursors.add( new TimelineCursorModel( freshCursor ) );
- }
- }
-
- for ( var n = 0; n < freshAnnotations.length; n++ ) {
- var freshAnnotation = freshAnnotations[ n ];
- var oldAnnotation = this._annotations.valueFor( freshAnnotation.annotationGuid );
- if ( hasval( oldAnnotation ) ) {
- oldAnnotation.setAttrs( freshAnnotation );
- }
- else {
- this._annotations.add( new TimelineAnnotationModel( freshAnnotation ) );
- }
- }
-
- for ( var n = 0; n < freshTimeseriesFragments.length; n++ ) {
- var freshTimeseriesFragment = freshTimeseriesFragments[ n ];
- var oldTimeseriesFragment = this._timeseriesFragments.valueFor( freshTimeseriesFragment.fragmentGuid );
- if ( hasval( oldTimeseriesFragment ) ) {
- oldTimeseriesFragment.setAttrs( freshTimeseriesFragment );
- }
- else {
- this._timeseriesFragments.add( new TimelineTimeseriesFragmentModel( freshTimeseriesFragment ) );
- }
- }
-
- for ( var n = 0; n < freshTimeseriesSet.length; n++ ) {
- var freshTimeseries = freshTimeseriesSet[ n ];
- var oldTimeseries = this._timeseries.valueFor( freshTimeseries.timeseriesGuid );
- if ( hasval( oldTimeseries ) ) {
- oldTimeseries.setAttrs( freshTimeseries );
- }
- else {
- this._timeseries.add( new TimelineTimeseriesModel( freshTimeseries ) );
- }
- }
+}
- for ( var n = 0; n < freshEvents.length; n++ ) {
- var freshEvent = freshEvents[ n ];
- var oldEvent = this._events.valueFor( freshEvent.eventGuid );
- if ( hasval( oldEvent ) ) {
- oldEvent.setAttrs( freshEvent );
- }
- else {
- this._events.add( new TimelineEventModel( freshEvent ) );
- }
- }
- for ( var n = 0; n < freshRows.length; n++ ) {
- var freshRow = freshRows[ n ];
- var oldRow = this._rows.valueFor( freshRow.rowGuid );
- if ( hasval( oldRow ) ) {
- oldRow.setAttrs( freshRow );
- oldRow.eventGuids.addAll( ( freshRow.eventGuids || [] ), 0, true );
- }
- else {
- this._rows.add( new TimelineRowModel( freshRow ) );
- }
- }
+export function newEmptyTimelineRoot(): TimelineRoot {
+ return {
+ groupGuids: [],
+ bottomPinnedRowGuids: [],
+ topPinnedRowGuids: [],
+ maximizedRowGuids: []
+ };
+}
- for ( var n = 0; n < freshGroups.length; n++ ) {
- var freshGroup = freshGroups[ n ];
- var oldGroup = this._groups.valueFor( freshGroup.groupGuid );
- if ( hasval( oldGroup ) ) {
- oldGroup.setAttrs( freshGroup );
- oldGroup.rowGuids.addAll( freshGroup.rowGuids, 0, true );
- }
- else {
- this._groups.add( new TimelineGroupModel( freshGroup ) );
- }
- }
- this._root.groupGuids.addAll( freshRoot.groupGuids, 0, true );
- this._root.topPinnedRowGuids.addAll( freshRoot.topPinnedRowGuids, 0, true );
- this._root.bottomPinnedRowGuids.addAll( freshRoot.bottomPinnedRowGuids, 0, true );
- this._root.maximizedRowGuids.addAll( freshRoot.maximizedRowGuids, 0, true );
- }
+export let timelineMergeNewBeforeOld: TimelineMergeStrategy = {
+ updateCursorModel(cursorModel: TimelineCursorModel, newCursor: TimelineCursor) {
+ cursorModel.setAttrs(newCursor);
+ },
- merge( newData : Timeline, strategy : TimelineMergeStrategy ) {
-
- var newCursors = hasval( newData.cursors ) ? newData.cursors : [];
- for ( var n = 0; n < newCursors.length; n++ ) {
- var newCursor = newCursors[ n ];
- var cursorModel = this._cursors.valueFor( newCursor.cursorGuid );
- if ( hasval( cursorModel ) ) {
- strategy.updateCursorModel( cursorModel, newCursor );
- }
- else {
- this._cursors.add( new TimelineCursorModel( newCursor ) );
- }
- }
+ updateAnnotationModel(annotationModel: TimelineAnnotationModel, newAnnotation: TimelineAnnotation) {
+ annotationModel.setAttrs(newAnnotation);
+ },
- var newAnnotations = hasval( newData.annotations ) ? newData.annotations : [];
- for ( var n = 0; n < newAnnotations.length; n++ ) {
- var newAnnotation = newAnnotations[ n ];
- var annotationModel = this._annotations.valueFor( newAnnotation.annotationGuid );
- if ( hasval( annotationModel ) ) {
- strategy.updateAnnotationModel( annotationModel, newAnnotation );
- }
- else {
- this._annotations.add( new TimelineAnnotationModel( newAnnotation ) );
- }
- }
-
- var newTimeseriesFragments = hasval( newData.timeseriesFragments ) ? newData.timeseriesFragments : [];
- for ( var n = 0; n < newTimeseriesFragments.length; n++ ) {
- var newTimeseriesFragment = newTimeseriesFragments[ n ];
- var timeseriesFragmentModel = this._timeseriesFragments.valueFor( newTimeseriesFragment.fragmentGuid );
- if ( hasval( timeseriesFragmentModel ) ) {
- strategy.updateTimeseriesFragmentModel( timeseriesFragmentModel, newTimeseriesFragment );
- }
- else {
- this._timeseriesFragments.add( new TimelineTimeseriesFragmentModel( newTimeseriesFragment ) );
- }
- }
-
- var newTimeseriesSet = hasval( newData.timeseries ) ? newData.timeseries : [];
- for ( var n = 0; n < newTimeseriesSet.length; n++ ) {
- var newTimeseries = newTimeseriesSet[ n ];
- var timeseriesModel = this._timeseries.valueFor( newTimeseries.timeseriesGuid );
- if ( hasval( timeseriesModel ) ) {
- strategy.updateTimeseriesModel( timeseriesModel, newTimeseries );
- }
- else {
- this._timeseries.add( new TimelineTimeseriesModel( newTimeseries ) );
- }
- }
-
- var newEvents = hasval( newData.events ) ? newData.events : [];
- for ( var n = 0; n < newEvents.length; n++ ) {
- var newEvent = newEvents[ n ];
- var eventModel = this._events.valueFor( newEvent.eventGuid );
- if ( hasval( eventModel ) ) {
- strategy.updateEventModel( eventModel, newEvent );
- }
- else {
- this._events.add( new TimelineEventModel( newEvent ) );
- }
- }
+ updateTimeseriesFragmentModel(timeseriesFragmentModel: TimelineTimeseriesFragmentModel, newTimeseriesFragment: TimelineTimeseriesFragment) {
+ timeseriesFragmentModel.setAttrs(newTimeseriesFragment);
+ },
- var newRows = hasval( newData.rows ) ? newData.rows : [];
- for ( var n = 0; n < newRows.length; n++ ) {
- var newRow = newRows[ n ];
- var rowModel = this._rows.valueFor( newRow.rowGuid );
- if ( hasval( rowModel ) ) {
- strategy.updateRowModel( rowModel, newRow );
- }
- else {
- this._rows.add( new TimelineRowModel( newRow ) );
- }
- }
+ updateTimeseriesModel(timeseriesModel: TimelineTimeseriesModel, newTimeseries: TimelineTimeseries) {
+ timeseriesModel.setAttrs(newTimeseries);
+ timeseriesModel.fragmentGuids.addAll((newTimeseries.fragmentGuids || []), 0, true);
+ },
- var newGroups = hasval( newData.groups ) ? newData.groups : [];
- for ( var n = 0; n < newGroups.length; n++ ) {
- var newGroup = newGroups[ n ];
- var groupModel = this._groups.valueFor( newGroup.groupGuid );
- if ( hasval( groupModel ) ) {
- strategy.updateGroupModel( groupModel, newGroup );
- }
- else {
- this._groups.add( new TimelineGroupModel( newGroup ) );
- }
- }
+ updateEventModel: function (eventModel: TimelineEventModel, newEvent: TimelineEvent) {
+ eventModel.setAttrs(newEvent);
+ },
- var newRoot = newData.root;
- strategy.updateRootModel( this._root, newRoot );
- }
-
- snapshot( ) : Timeline {
- return {
- cursors : this._cursors.map( (e)=>e.snapshot() ),
- annotations : this._annotations.map( (e)=>e.snapshot() ),
- timeseriesFragments : this._timeseriesFragments.map( (e)=>e.snapshot() ),
- timeseries : this._timeseries.map( (e)=>e.snapshot() ),
- events : this._events.map( (e)=>e.snapshot() ),
- rows : this._rows.map( (r)=>r.snapshot() ),
- groups : this._groups.map( (g)=>g.snapshot() ),
- root : this._root.snapshot( )
- };
- }
-
-
- }
-
-
- export function newEmptyTimelineRoot( ) : TimelineRoot {
- return { groupGuids: [],
- bottomPinnedRowGuids: [],
- topPinnedRowGuids: [],
- maximizedRowGuids: [] };
- }
-
-
- export var timelineMergeNewBeforeOld : TimelineMergeStrategy = {
-
- updateCursorModel( cursorModel : TimelineCursorModel, newCursor : TimelineCursor ) {
- cursorModel.setAttrs( newCursor );
- },
-
- updateAnnotationModel( annotationModel : TimelineAnnotationModel, newAnnotation : TimelineAnnotation ) {
- annotationModel.setAttrs( newAnnotation );
- },
-
- updateTimeseriesFragmentModel( timeseriesFragmentModel : TimelineTimeseriesFragmentModel, newTimeseriesFragment : TimelineTimeseriesFragment ) {
- timeseriesFragmentModel.setAttrs( newTimeseriesFragment );
- },
-
- updateTimeseriesModel( timeseriesModel : TimelineTimeseriesModel, newTimeseries : TimelineTimeseries ) {
- timeseriesModel.setAttrs( newTimeseries );
- timeseriesModel.fragmentGuids.addAll( ( newTimeseries.fragmentGuids || [] ), 0, true );
- },
-
- updateEventModel: function( eventModel : TimelineEventModel, newEvent : TimelineEvent ) {
- eventModel.setAttrs( newEvent );
- },
-
- updateRowModel: function( rowModel : TimelineRowModel, newRow : TimelineRow ) {
- rowModel.setAttrs( newRow );
- rowModel.eventGuids.addAll( ( newRow.eventGuids || [] ), 0, true );
- },
-
- updateGroupModel: function( groupModel : TimelineGroupModel, newGroup : TimelineGroup ) {
- groupModel.setAttrs( newGroup );
- groupModel.rowGuids.addAll( newGroup.rowGuids, 0, true );
- },
-
- updateRootModel: function( rootModel : TimelineRootModel, newRoot : TimelineRoot ) {
- rootModel.setAttrs( newRoot );
- rootModel.groupGuids.addAll( newRoot.groupGuids, 0, true );
- rootModel.topPinnedRowGuids.addAll( newRoot.topPinnedRowGuids || [], 0, true );
- rootModel.bottomPinnedRowGuids.addAll( newRoot.bottomPinnedRowGuids || [], 0, true );
- rootModel.maximizedRowGuids.addAll( newRoot.maximizedRowGuids || [], 0, true );
- }
- };
+ updateRowModel: function (rowModel: TimelineRowModel, newRow: TimelineRow) {
+ rowModel.setAttrs(newRow);
+ rowModel.eventGuids.addAll((newRow.eventGuids || []), 0, true);
+ },
+ updateGroupModel: function (groupModel: TimelineGroupModel, newGroup: TimelineGroup) {
+ groupModel.setAttrs(newGroup);
+ groupModel.rowGuids.addAll(newGroup.rowGuids, 0, true);
+ },
- export var timelineMergeNewAfterOld : TimelineMergeStrategy = {
-
- updateCursorModel( cursorModel : TimelineCursorModel, newCursor : TimelineCursor ) {
- cursorModel.setAttrs( newCursor );
- },
-
- updateAnnotationModel( annotationModel : TimelineAnnotationModel, newAnnotation : TimelineAnnotation ) {
- annotationModel.setAttrs( newAnnotation );
- },
-
- updateTimeseriesFragmentModel( timeseriesFragmentModel : TimelineTimeseriesFragmentModel, newTimeseriesFragment : TimelineTimeseriesFragment ) {
- timeseriesFragmentModel.setAttrs( newTimeseriesFragment );
- },
-
- updateTimeseriesModel( timeseriesModel : TimelineTimeseriesModel, newTimeseries : TimelineTimeseries ) {
- timeseriesModel.setAttrs( newTimeseries );
- timeseriesModel.fragmentGuids.addAll( newTimeseries.fragmentGuids || [] );
- },
-
- updateEventModel: function( eventModel : TimelineEventModel, newEvent : TimelineEvent ) {
- eventModel.setAttrs( newEvent );
- },
-
- updateRowModel: function( rowModel : TimelineRowModel, newRow : TimelineRow ) {
- rowModel.setAttrs( newRow );
- rowModel.eventGuids.addAll( newRow.eventGuids || [] );
- rowModel.timeseriesGuids.addAll( newRow.timeseriesGuids || [] );
- rowModel.annotationGuids.addAll( newRow.annotationGuids || [] );
- },
-
- updateGroupModel: function( groupModel : TimelineGroupModel, newGroup : TimelineGroup ) {
- groupModel.setAttrs( newGroup );
- groupModel.rowGuids.addAll( newGroup.rowGuids );
- },
-
- updateRootModel: function( rootModel : TimelineRootModel, newRoot : TimelineRoot ) {
- rootModel.setAttrs( newRoot );
- rootModel.groupGuids.addAll( newRoot.groupGuids );
- rootModel.topPinnedRowGuids.addAll( newRoot.topPinnedRowGuids || [] );
- rootModel.bottomPinnedRowGuids.addAll( newRoot.bottomPinnedRowGuids || [] );
- rootModel.maximizedRowGuids.addAll( newRoot.maximizedRowGuids || [] );
- }
- };
-}
\ No newline at end of file
+ updateRootModel: function (rootModel: TimelineRootModel, newRoot: TimelineRoot) {
+ rootModel.setAttrs(newRoot);
+ rootModel.groupGuids.addAll(newRoot.groupGuids, 0, true);
+ rootModel.topPinnedRowGuids.addAll(newRoot.topPinnedRowGuids || [], 0, true);
+ rootModel.bottomPinnedRowGuids.addAll(newRoot.bottomPinnedRowGuids || [], 0, true);
+ rootModel.maximizedRowGuids.addAll(newRoot.maximizedRowGuids || [], 0, true);
+ }
+};
+
+
+export let timelineMergeNewAfterOld: TimelineMergeStrategy = {
+
+ updateCursorModel(cursorModel: TimelineCursorModel, newCursor: TimelineCursor) {
+ cursorModel.setAttrs(newCursor);
+ },
+
+ updateAnnotationModel(annotationModel: TimelineAnnotationModel, newAnnotation: TimelineAnnotation) {
+ annotationModel.setAttrs(newAnnotation);
+ },
+
+ updateTimeseriesFragmentModel(timeseriesFragmentModel: TimelineTimeseriesFragmentModel, newTimeseriesFragment: TimelineTimeseriesFragment) {
+ timeseriesFragmentModel.setAttrs(newTimeseriesFragment);
+ },
+
+ updateTimeseriesModel(timeseriesModel: TimelineTimeseriesModel, newTimeseries: TimelineTimeseries) {
+ timeseriesModel.setAttrs(newTimeseries);
+ timeseriesModel.fragmentGuids.addAll(newTimeseries.fragmentGuids || []);
+ },
+
+ updateEventModel: function (eventModel: TimelineEventModel, newEvent: TimelineEvent) {
+ eventModel.setAttrs(newEvent);
+ },
+
+ updateRowModel: function (rowModel: TimelineRowModel, newRow: TimelineRow) {
+ rowModel.setAttrs(newRow);
+ rowModel.eventGuids.addAll(newRow.eventGuids || []);
+ rowModel.timeseriesGuids.addAll(newRow.timeseriesGuids || []);
+ rowModel.annotationGuids.addAll(newRow.annotationGuids || []);
+ },
+
+ updateGroupModel: function (groupModel: TimelineGroupModel, newGroup: TimelineGroup) {
+ groupModel.setAttrs(newGroup);
+ groupModel.rowGuids.addAll(newGroup.rowGuids);
+ },
+
+ updateRootModel: function (rootModel: TimelineRootModel, newRoot: TimelineRoot) {
+ rootModel.setAttrs(newRoot);
+ rootModel.groupGuids.addAll(newRoot.groupGuids);
+ rootModel.topPinnedRowGuids.addAll(newRoot.topPinnedRowGuids || []);
+ rootModel.bottomPinnedRowGuids.addAll(newRoot.bottomPinnedRowGuids || []);
+ rootModel.maximizedRowGuids.addAll(newRoot.maximizedRowGuids || []);
+ }
+};
diff --git a/src/webglimpse/timeline/timeline_pane.ts b/src/webglimpse/timeline/timeline_pane.ts
index d5f4fa8..64c7765 100644
--- a/src/webglimpse/timeline/timeline_pane.ts
+++ b/src/webglimpse/timeline/timeline_pane.ts
@@ -27,1362 +27,1495 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Pane, Layout, Drawable, PointerEvent, xFrac, Mask2D, Painter, isLeftMouseDown, LayoutPhase1 } from '../core';
+import { TimelineModel, TimelineRowModel, TimelineGroupModel, TimelineGroup } from './timeline_model';
+import { TimelineUi, TimeIntervalModel, TimelineInput } from './timeline_ui';
+import { TimelineRowPaneFactoryChooser, TimelineRowPaneFactory } from './timeline_row';
+import { ScrollbarOptions, newVerticalScrollLayout, newVerticalScrollbar, attachTimelineVerticalScrollMouseListeners, VerticalScrollLayout } from '../scroll';
+import { Color, white, rgb, gray, rgba } from '../color';
+import { TimeAxisFormatOptions, newTimeAxisPainter } from './time_axis_painter';
+import { Insets, newInsets, newInsetPane, newInsetLayout } from '../layout/inset_layout';
+import { TimeAxis1D } from './time_axis';
+import { rowPaneFactoryChooser_DEFAULT } from './timeline_styles';
+import { axisZoomStep, Axis1D } from '../plot/axis';
+import { newColumnLayout } from '../layout/column_layout';
+import { newCardLayout } from '../layout/card_layout';
+import { newRowLayout } from '../layout/row_layout';
+import { Side, newBackgroundPainter, xyFrac_VERTSHADER, solid_FRAGSHADER, putQuadXys, newSolidPane, Background, Highlight } from '../misc';
+import { newOverlayLayout } from '../layout/overlay_layout';
+import { alwaysTrue, GL, StringMap, hasval, isNumber } from '../util/util';
+import { BoundsUnmodifiable, Size } from '../bounds';
+import { Program, Attribute, UniformColor } from '../shader';
+import { newDynamicBuffer } from '../buffer';
+import { TextureRenderer } from '../texture';
+import { Label, fitToLabel, newLabelPainter } from '../label';
+import { OrderedStringSet } from '../util/ordered_set';
+import { newTimeGridPainter } from './time_grid_painter';
+import { newBorderPainter } from '../painter/border_painter';
+import { createTextTextureFactory } from '../text';
+
+export class TimelinePane extends Pane {
+ private _model: TimelineModel;
+ private _ui: TimelineUi;
+
+ constructor(layout: Layout, model: TimelineModel, ui: TimelineUi) {
+ super(layout, true);
+ this._model = model;
+ this._ui = ui;
+ }
+
+ get model(): TimelineModel { return this._model; }
+ get ui(): TimelineUi { return this._ui; }
+}
- export class TimelinePane extends Pane {
- private _model : TimelineModel;
- private _ui : TimelineUi;
- constructor( layout : Layout, model : TimelineModel, ui : TimelineUi ) {
- super( layout, true );
- this._model = model;
- this._ui = ui;
+
+export interface TimelinePaneOptions {
+
+ // Misc
+ font?: string;
+ rowPaneFactoryChooser?: TimelineRowPaneFactoryChooser;
+ cursor?: string;
+
+ // Scroll
+ showScrollbar?: boolean;
+ scrollbarOptions?: ScrollbarOptions;
+
+ // Colors
+ fgColor?: Color;
+ rowLabelColor?: Color;
+ rowLabelBgColor?: Color;
+ rowHighlightColor?: Color;
+ groupLabelColor?: Color;
+ groupHighlightColor?: Color;
+ axisLabelColor?: Color;
+ bgColor?: Color;
+ rowBgColor?: Color;
+ rowAltBgColor?: Color;
+ gridColor?: Color;
+ selectedIntervalFillColor?: Color;
+ selectedIntervalBorderColor?: Color;
+
+ // Axes
+ showTopAxis?: boolean;
+ showBottomAxis?: boolean;
+ topTimeZone?: string;
+ bottomTimeZone?: string;
+ tickSpacing?: number;
+ axisLabelAlign?: number;
+ isFuturePositive?: boolean;
+ timeAxisFormat?: TimeAxisFormatOptions;
+
+ // date in time_ISO8601 format (see util.ts:parseTime_PMILLIS)
+ // that time labels on the timeline should be referenced from
+ // (i.e. Day 1, Day 2...)
+ // (if not provided, timeline labels will be in absolute time)
+ referenceDate?: string;
+
+ // Sizing
+ groupLabelInsets?: Insets;
+ groupHighlightWidth?: number;
+ groupHighlightInsets?: Insets;
+ rowLabelInsets?: Insets;
+ rowLabelPaneWidth?: number;
+ rowSeparatorHeight?: number;
+ rowHighlightWidth?: number;
+ rowHighlightInsets?: Insets;
+ scrollbarWidth?: number;
+ axisPaneHeight?: number;
+ draggableEdgeWidth?: number;
+ snapToDistance?: number;
+
+ // Event / Selection
+ allowEventMultiSelection?: boolean;
+ // Options:
+ // 'none' or any falsy value : no time selection
+ // 'single' : single selected time
+ // 'range' : range of selected times
+ // 'single-unmodifiable' : single selected time (cannot be adjusted by user mouse clicks)
+ // 'range-unmodifiable' : range of selected times (cannot be adjusted by user mouse clicks)
+ selectedIntervalMode?: string;
+ // enable / disable centering of time selection interval on double click
+ // (this has no effect for selectedIntervalMode 'none', 'single-unmodifiable', or 'range-unmodifiable'
+ // because mouse controls are off by default in those modes)
+ centerSelectedIntervalOnDoubleClick?: boolean;
+ // provide a custom listener callback function to modify the default behavior when the mouse
+ // scroll wheel is rotate over the timeline (default behavior is to zoom in/out in time)
+ mouseWheelListener?: (PointerEvent: PointerEvent) => void;
+}
+
+export function newTimelinePane(drawable: Drawable, timeAxis: TimeAxis1D, model: TimelineModel, options?: TimelinePaneOptions, ui?: TimelineUi): TimelinePane {
+
+ // Misc
+ const font = (hasval(options) && hasval(options.font) ? options.font : '11px verdana,sans-serif');
+ const rowPaneFactoryChooser = (hasval(options) && hasval(options.rowPaneFactoryChooser) ? options.rowPaneFactoryChooser : rowPaneFactoryChooser_DEFAULT);
+ const cursor = (hasval(options) && hasval(options.cursor) ? options.cursor : 'auto');
+
+ // Scroll
+ const showScrollbar = (hasval(options) && hasval(options.showScrollbar) ? options.showScrollbar : true);
+ const scrollbarOptions = (hasval(options) ? options.scrollbarOptions : null);
+
+ // Colors
+ const fgColor = (hasval(options) && hasval(options.fgColor) ? options.fgColor : white);
+ const bgColor = (hasval(options) && hasval(options.bgColor) ? options.bgColor : rgb(0.098, 0.165, 0.243));
+ const rowLabelColor = (hasval(options) && hasval(options.rowLabelColor) ? options.rowLabelColor : fgColor);
+ const rowLabelBgColor = (hasval(options) && hasval(options.rowLabelBgColor) ? options.rowLabelBgColor : bgColor);
+ const rowHighlightColor = (hasval(options) && hasval(options.rowHighlightColor) ? options.rowHighlightColor : rgb(1, 0.623, 0));
+ const groupLabelColor = (hasval(options) && hasval(options.groupLabelColor) ? options.groupLabelColor : fgColor);
+ const groupHighlightColor = (hasval(options) && hasval(options.groupHighlightColor) ? options.groupHighlightColor : rgb(0, 1, 1));
+ const axisLabelColor = (hasval(options) && hasval(options.axisLabelColor) ? options.axisLabelColor : fgColor);
+ const rowBgColor = (hasval(options) && hasval(options.rowBgColor) ? options.rowBgColor : rgb(0.020, 0.086, 0.165));
+ const rowAltBgColor = (hasval(options) && hasval(options.rowAltBgColor) ? options.rowAltBgColor : rgb(0.020, 0.086, 0.165));
+ const gridColor = (hasval(options) && hasval(options.gridColor) ? options.gridColor : gray(0.5));
+ const selectedIntervalFillColor = (hasval(options) && hasval(options.selectedIntervalFillColor) ? options.selectedIntervalFillColor : rgba(0, 0.6, 0.8, 0.157));
+ const selectedIntervalBorderColor = (hasval(options) && hasval(options.selectedIntervalBorderColor) ? options.selectedIntervalBorderColor : rgb(0, 0.2, 1.0));
+
+ // Axes
+ const showTopAxis = (hasval(options) && hasval(options.showTopAxis) ? options.showTopAxis : true);
+ const showBottomAxis = (hasval(options) && hasval(options.showBottomAxis) ? options.showBottomAxis : true);
+ const topTimeZone = (hasval(options) && hasval(options.topTimeZone) ? options.topTimeZone : '+0000');
+ const bottomTimeZone = (hasval(options) && hasval(options.bottomTimeZone) ? options.bottomTimeZone : '+0000');
+ const tickSpacing = (hasval(options) && hasval(options.tickSpacing) ? options.tickSpacing : 60);
+ const axisLabelAlign = (hasval(options) && hasval(options.axisLabelAlign) ? options.axisLabelAlign : 0.5);
+
+ // Sizing
+ const groupLabelInsets = (hasval(options) && hasval(options.groupLabelInsets) ? options.groupLabelInsets : newInsets(6, 10));
+ const groupHighlightWidth = (hasval(options) && hasval(options.groupHighlightWidth) ? options.groupHighlightWidth : 2);
+ const groupHighlightInsets = (hasval(options) && hasval(options.groupHighlightInsets) ? options.groupHighlightInsets : newInsets(6, 0, 2, 4));
+ const rowLabelInsets = (hasval(options) && hasval(options.rowLabelInsets) ? options.rowLabelInsets : newInsets(0, 35));
+ const rowLabelPaneWidth = (hasval(options) && hasval(options.rowLabelPaneWidth) ? options.rowLabelPaneWidth : 140);
+ const rowHighlightWidth = (hasval(options) && hasval(options.rowHighlightWidth) ? options.rowHighlightWidth : 2);
+ const rowHighlightInsets = (hasval(options) && hasval(options.rowHighlightInsets) ? options.rowHighlightInsets : newInsets(1, 0, 2, 10));
+ const rowSeparatorHeight = (hasval(options) && hasval(options.rowSeparatorHeight) ? options.rowSeparatorHeight : 2);
+ let scrollbarWidth = (hasval(options) && hasval(options.scrollbarWidth) ? options.scrollbarWidth : 16);
+ scrollbarWidth = showScrollbar ? scrollbarWidth : 0; // if the scrollbar is not showing, set its width to 0
+ const axisPaneHeight = (hasval(options) && hasval(options.axisPaneHeight) ? options.axisPaneHeight : 40);
+ const draggableEdgeWidth = (hasval(options) && hasval(options.draggableEdgeWidth) ? options.draggableEdgeWidth : 6);
+ const snapToDistance = (hasval(options) && hasval(options.snapToDistance) ? options.snapToDistance : 10);
+
+ // Event / Selection
+ const allowEventMultiSelection = (hasval(options) && hasval(options.allowEventMultiSelection) ? options.allowEventMultiSelection : true);
+ const selectedIntervalMode = (hasval(options) && hasval(options.selectedIntervalMode) ? options.selectedIntervalMode : 'range');
+ const centerSelectedIntervalOnDoubleClick = (hasval(options) && hasval(options.centerSelectedIntervalOnDoubleClick) ? options.centerSelectedIntervalOnDoubleClick : true);
+ const defaultMouseWheelListener = function (ev: PointerEvent) {
+ const zoomFactor = Math.pow(axisZoomStep, ev.wheelSteps);
+ timeAxis.zoom(zoomFactor, timeAxis.vAtFrac(xFrac(ev)));
+ };
+ const mouseWheelListener = (hasval(options) && hasval(options.mouseWheelListener) ? options.mouseWheelListener : defaultMouseWheelListener);
+
+ let outsideManagedUi = false;
+ if (!ui) {
+ outsideManagedUi = false;
+ ui = new TimelineUi(model, { allowEventMultiSelection: allowEventMultiSelection });
+ }
+ else {
+ // remove old panes (if the ui is being reused)
+ outsideManagedUi = true;
+ ui.panes.removeAll();
+ }
+
+ const selection = ui.selection;
+
+ const redraw = function () {
+ drawable.redraw();
+ };
+ selection.selectedInterval.changed.on(redraw);
+ selection.hoveredEvent.changed.on(redraw);
+ selection.selectedEvents.valueAdded.on(redraw);
+ selection.selectedEvents.valueRemoved.on(redraw);
+
+ // even if the model defines cursors, we may need to redraw when the mouse position changes
+ // (we might not actually need to if: none of the rows actually use the cursor, or if the
+ // cursor doesn't show a vertical or horizontal line)
+ // this check just avoids redrawing unncessarily in the easy-to-verify common case where
+ // no cursors are defined
+ const redrawCursor = function () {
+ if (!model.cursors.isEmpty) {
+ drawable.redraw();
}
+ };
+
+ selection.hoveredY.changed.on(redrawCursor);
+ selection.hoveredTime_PMILLIS.changed.on(redrawCursor);
+
+ // Scroll Pane and Maximized Row Pane
+ //
+
+ // setup Pane which either shows timeline content, or only maximized rows
+ // able to switch between the two depending on model.root.maximizedRowGuids.isEmpty
+
+ // Scroll Pane
+
+ const tickTimeZone = (showTopAxis ? topTimeZone : bottomTimeZone);
+ const contentPaneOpts = { selectedIntervalMode: selectedIntervalMode, rowPaneFactoryChooser: rowPaneFactoryChooser, font: font, fgColor: fgColor, rowLabelColor: rowLabelColor, rowLabelBgColor: rowLabelBgColor, rowHighlightColor: rowHighlightColor, groupLabelColor: groupLabelColor, groupHighlightColor: groupHighlightColor, axisLabelColor: axisLabelColor, bgColor: bgColor, rowBgColor: rowBgColor, rowAltBgColor: rowAltBgColor, gridColor: gridColor, gridTickSpacing: tickSpacing, gridTimeZone: tickTimeZone, referenceDate: options.referenceDate, groupLabelInsets: groupLabelInsets, groupHighlightWidth: groupHighlightWidth, groupHighlightInsets: groupHighlightInsets, rowLabelInsets: rowLabelInsets, rowLabelPaneWidth: rowLabelPaneWidth, rowHighlightWidth: rowHighlightWidth, rowHighlightInsets: rowHighlightInsets, rowSeparatorHeight: rowSeparatorHeight, draggableEdgeWidth: draggableEdgeWidth, snapToDistance: snapToDistance, mouseWheelListener: mouseWheelListener };
+ let contentPaneArgs;
+ let contentPane: Pane = null;
+
+ if (showScrollbar) {
+
+ const scrollLayout = newVerticalScrollLayout();
+ const scrollable = new Pane(scrollLayout, false);
+ ui.addPane('scroll-content-pane', scrollable);
+
+ contentPaneArgs = { drawable: drawable, scrollLayout: scrollLayout, timeAxis: timeAxis, model: model, ui: ui, options: contentPaneOpts };
+
+ const scrollContentPane = newTimelineContentPane(contentPaneArgs);
+ ui.addPane('content-pane', scrollContentPane);
+
+ scrollable.addPane(scrollContentPane, 0);
+
+ const scrollbar = newVerticalScrollbar(scrollLayout, drawable, scrollbarOptions);
+ ui.addPane('scrollbar', scrollbar);
+
+ contentPane = new Pane(newColumnLayout(false), false);
+ ui.addPane('scroll-outer-pane', contentPane);
+
+ contentPane.addPane(scrollbar, 0, { width: scrollbarWidth, ignoreHeight: true });
+ contentPane.addPane(scrollable, 1);
+
+ }
+ else {
+
+ contentPaneArgs = { drawable: drawable, scrollLayout: null, timeAxis: timeAxis, model: model, ui: ui, options: contentPaneOpts };
+ contentPane = newTimelineContentPane(contentPaneArgs);
+ ui.addPane('content-pane', contentPane);
- get model( ) : TimelineModel { return this._model; }
- get ui( ) : TimelineUi { return this._ui; }
}
+ // Card Pane Switching Logic
+
+ const timelineCardPane = new Pane(newCardLayout());
+ ui.addPane('switch-content-pane', timelineCardPane);
+
+ const maximizedContentPane = new Pane(newRowLayout());
+ ui.addPane('maximize-content-pane', maximizedContentPane);
+ const insetMaximizedContentPane = newInsetPane(maximizedContentPane, newInsets(0, scrollbarWidth, 0, 0));
+ ui.addPane('inset-maximize-content-pane', insetMaximizedContentPane);
- export interface TimelinePaneOptions {
-
- // Misc
- font? : string;
- rowPaneFactoryChooser? : TimelineRowPaneFactoryChooser;
-
- // Scroll
- showScrollbar? : boolean;
- scrollbarOptions? : ScrollbarOptions;
-
- // Colors
- fgColor? : Color;
- rowLabelColor? : Color;
- rowLabelBgColor? : Color;
- groupLabelColor? : Color;
- axisLabelColor? : Color;
- bgColor? : Color;
- rowBgColor? : Color;
- rowAltBgColor? : Color;
- gridColor? : Color;
- selectedIntervalFillColor? : Color;
- selectedIntervalBorderColor? : Color;
-
- // Axes
- showTopAxis? : boolean;
- showBottomAxis? : boolean;
- topTimeZone? : string;
- bottomTimeZone? : string;
- tickSpacing? : number;
- axisLabelAlign? : number;
- isFuturePositive? : boolean;
-
- // date in time_ISO8601 format (see util.ts:parseTime_PMILLIS)
- // that time labels on the timeline should be referenced from
- // (i.e. Day 1, Day 2...)
- // (if not provided, timeline labels will be in absolute time)
- referenceDate? : string;
-
- // Sizing
- groupLabelInsets? : Insets;
- rowLabelInsets? : Insets;
- rowLabelPaneWidth? : number;
- rowSeparatorHeight? : number;
- scrollbarWidth? : number;
- axisPaneHeight? : number;
- draggableEdgeWidth? : number;
- snapToDistance? : number;
-
- // Event / Selection
- allowEventMultiSelection? : boolean;
- // Options:
- // 'none' or any falsy value : no time selection
- // 'single' : single selected time
- // 'range' : range of selected times
- // 'single-unmodifiable' : single selected time (cannot be adjusted by user mouse clicks)
- // 'range-unmodifiable' : range of selected times (cannot be adjusted by user mouse clicks)
- selectedIntervalMode? : string;
- // enable / disable centering of time selection interval on double click
- // (this has no effect for selectedIntervalMode 'none', 'single-unmodifiable', or 'range-unmodifiable'
- // because mouse controls are off by default in those modes)
- centerSelectedIntervalOnDoubleClick? : boolean;
- // provide a custom listener callback function to modify the default behavior when the mouse
- // scroll wheel is rotate over the timeline (default behavior is to zoom in/out in time)
- mouseWheelListener? : ( PointerEvent ) => void;
+ const contentActive = model.root.maximizedRowGuids.isEmpty;
+ timelineCardPane.addPane(insetMaximizedContentPane, !contentActive);
+ timelineCardPane.addPane(contentPane, contentActive);
+
+ setupRowContainerPane(contentPaneArgs, maximizedContentPane, model.root.maximizedRowGuids, true, 'maximized');
+
+ const updateMaximizedRows = function (rowGuid: string, rowIndex: number) {
+ const contentActiveInner = model.root.maximizedRowGuids.isEmpty;
+ timelineCardPane.setLayoutArg(insetMaximizedContentPane, !contentActiveInner);
+ timelineCardPane.setLayoutArg(contentPane, contentActiveInner);
+ drawable.redraw();
+ };
+
+ model.root.maximizedRowGuids.valueAdded.on(updateMaximizedRows);
+ model.root.maximizedRowGuids.valueRemoved.on(updateMaximizedRows);
+
+ // Overlay and Underlay Panes
+ //
+
+ const underlayPane = new Pane(newRowLayout());
+ ui.addPane('underlay-pane', underlayPane);
+
+ const axisInsets = newInsets(0, scrollbarWidth, 0, rowLabelPaneWidth);
+
+ // top time axis pane
+ const axisOpts = { tickSpacing: tickSpacing, font: font, textColor: axisLabelColor, tickColor: axisLabelColor, labelAlign: axisLabelAlign, referenceDate: options.referenceDate, isFuturePositive: options.isFuturePositive, timeAxisFormat: options.timeAxisFormat };
+ if (showTopAxis) {
+ const topAxisPane = newTimeAxisPane(contentPaneArgs, null, cursor);
+ ui.addPane('top-axis-pane', topAxisPane);
+ topAxisPane.addPainter(newTimeAxisPainter(timeAxis, Side.TOP, topTimeZone, tickTimeZone, axisOpts));
+ underlayPane.addPane(newInsetPane(topAxisPane, axisInsets), 0, { height: axisPaneHeight, width: null });
}
- export function newTimelinePane( drawable : Drawable, timeAxis : TimeAxis1D, model : TimelineModel, options? : TimelinePaneOptions, ui? : TimelineUi ) : TimelinePane {
-
- // Misc
- var font = ( hasval( options ) && hasval( options.font ) ? options.font : '11px verdana,sans-serif' );
- var rowPaneFactoryChooser = ( hasval( options ) && hasval( options.rowPaneFactoryChooser ) ? options.rowPaneFactoryChooser : rowPaneFactoryChooser_DEFAULT );
-
- // Scroll
- var showScrollbar = ( hasval( options ) && hasval( options.showScrollbar ) ? options.showScrollbar : true );
- var scrollbarOptions = ( hasval( options ) ? options.scrollbarOptions : null );
-
- // Colors
- var fgColor = ( hasval( options ) && hasval( options.fgColor ) ? options.fgColor : white );
- var bgColor = ( hasval( options ) && hasval( options.bgColor ) ? options.bgColor : rgb( 0.098, 0.165, 0.243 ) );
- var rowLabelColor = ( hasval( options ) && hasval( options.rowLabelColor ) ? options.rowLabelColor : fgColor );
- var rowLabelBgColor = ( hasval( options ) && hasval( options.rowLabelBgColor ) ? options.rowLabelBgColor : bgColor );
- var groupLabelColor = ( hasval( options ) && hasval( options.groupLabelColor ) ? options.groupLabelColor : fgColor );
- var axisLabelColor = ( hasval( options ) && hasval( options.axisLabelColor ) ? options.axisLabelColor : fgColor );
- var rowBgColor = ( hasval( options ) && hasval( options.rowBgColor ) ? options.rowBgColor : rgb( 0.020, 0.086, 0.165 ) );
- var rowAltBgColor = ( hasval( options ) && hasval( options.rowAltBgColor ) ? options.rowAltBgColor : rgb( 0.020, 0.086, 0.165 ) );
- var gridColor = ( hasval( options ) && hasval( options.gridColor ) ? options.gridColor : gray( 0.5 ) );
- var selectedIntervalFillColor = ( hasval( options ) && hasval( options.selectedIntervalFillColor ) ? options.selectedIntervalFillColor : rgba( 0, 0.6, 0.8, 0.157 ) );
- var selectedIntervalBorderColor = ( hasval( options ) && hasval( options.selectedIntervalBorderColor ) ? options.selectedIntervalBorderColor : rgb( 0, 0.2, 1.0 ) );
-
- // Axes
- var showTopAxis = ( hasval( options ) && hasval( options.showTopAxis ) ? options.showTopAxis : true );
- var showBottomAxis = ( hasval( options ) && hasval( options.showBottomAxis ) ? options.showBottomAxis : true );
- var topTimeZone = ( hasval( options ) && hasval( options.topTimeZone ) ? options.topTimeZone : '+0000' );
- var bottomTimeZone = ( hasval( options ) && hasval( options.bottomTimeZone ) ? options.bottomTimeZone : '+0000' );
- var tickSpacing = ( hasval( options ) && hasval( options.tickSpacing ) ? options.tickSpacing : 60 );
- var axisLabelAlign = ( hasval( options ) && hasval( options.axisLabelAlign ) ? options.axisLabelAlign : 0.5 );
-
- // Sizing
- var groupLabelInsets = ( hasval( options ) && hasval( options.groupLabelInsets ) ? options.groupLabelInsets : newInsets( 6, 10 ) );
- var rowLabelInsets = ( hasval( options ) && hasval( options.rowLabelInsets ) ? options.rowLabelInsets : newInsets( 0, 35 ) );
- var rowLabelPaneWidth = ( hasval( options ) && hasval( options.rowLabelPaneWidth ) ? options.rowLabelPaneWidth : 140 );
- var rowSeparatorHeight = ( hasval( options ) && hasval( options.rowSeparatorHeight ) ? options.rowSeparatorHeight : 2 );
- var scrollbarWidth = ( hasval( options ) && hasval( options.scrollbarWidth ) ? options.scrollbarWidth : 16 );
- scrollbarWidth = showScrollbar ? scrollbarWidth : 0; // if the scrollbar is not showing, set its width to 0
- var axisPaneHeight = ( hasval( options ) && hasval( options.axisPaneHeight ) ? options.axisPaneHeight : 40 );
- var draggableEdgeWidth = ( hasval( options ) && hasval( options.draggableEdgeWidth ) ? options.draggableEdgeWidth : 6 );
- var snapToDistance = ( hasval( options ) && hasval( options.snapToDistance ) ? options.snapToDistance : 10 );
-
- // Event / Selection
- var allowEventMultiSelection = ( hasval( options ) && hasval( options.allowEventMultiSelection ) ? options.allowEventMultiSelection : true );
- var selectedIntervalMode = ( hasval( options ) && hasval( options.selectedIntervalMode ) ? options.selectedIntervalMode : 'range' );
- var centerSelectedIntervalOnDoubleClick = ( hasval( options ) && hasval( options.centerSelectedIntervalOnDoubleClick ) ? options.centerSelectedIntervalOnDoubleClick : true );
- var defaultMouseWheelListener = function( ev : PointerEvent ) {
- var zoomFactor = Math.pow( axisZoomStep, ev.wheelSteps );
- timeAxis.zoom( zoomFactor, timeAxis.vAtFrac( xFrac( ev ) ) );
- }
- var mouseWheelListener = ( hasval( options ) && hasval( options.mouseWheelListener ) ? options.mouseWheelListener : defaultMouseWheelListener);
+ // pane containing pinned rows specified in TimelineRoot.topPinnedRowGuids
+ const topPinnedPane = new Pane(newRowLayout());
+ ui.addPane('top-pinned-pane', topPinnedPane);
- if ( !ui ) {
- var outsideManagedUi = false;
- ui = new TimelineUi( model, { allowEventMultiSelection: allowEventMultiSelection } );
- }
- else {
- // remove old panes (if the ui is being reused)
- var outsideManagedUi = true;
- ui.panes.removeAll( );
- }
-
- var selection = ui.selection;
-
- var redraw = function( ) {
- drawable.redraw( );
- };
- selection.selectedInterval.changed.on( redraw );
- selection.hoveredEvent.changed.on( redraw );
- selection.selectedEvents.valueAdded.on( redraw );
- selection.selectedEvents.valueRemoved.on( redraw );
-
- // even if the model defines cursors, we may need to redraw when the mouse position changes
- // (we might not actually need to if: none of the rows actually use the cursor, or if the
- // cursor doesn't show a vertical or horizontal line)
- // this check just avoids redrawing unncessarily in the easy-to-verify common case where
- // no cursors are defined
- var redrawCursor = function( ) {
- if ( !model.cursors.isEmpty ) {
- drawable.redraw( );
+ const insetTopPinnedPane = newInsetPane(topPinnedPane, newInsets(0, scrollbarWidth, 0, 0));
+ ui.addPane('inset-top-pinned-pane', insetTopPinnedPane);
+
+ setupRowContainerPane(contentPaneArgs, topPinnedPane, model.root.topPinnedRowGuids, false, 'toppinned');
+ underlayPane.addPane(insetTopPinnedPane, 1);
+
+ // main pane containing timeline groups and rows
+ underlayPane.addPane(timelineCardPane, 2, { height: 'pref-max', width: null });
+
+ // pane containing pinned rows specified in TimelineRoot.bottomPinnedRowGuids
+ const bottomPinnedPane = new Pane(newRowLayout());
+ ui.addPane('bottom-pinned-pane', bottomPinnedPane);
+
+ const insetBottomPinnedPane = newInsetPane(bottomPinnedPane, newInsets(0, scrollbarWidth, 0, 0));
+ ui.addPane('inset-bottom-pinned-pane', insetBottomPinnedPane);
+
+ setupRowContainerPane(contentPaneArgs, bottomPinnedPane, model.root.bottomPinnedRowGuids, false, 'bottompinned');
+ underlayPane.addPane(insetBottomPinnedPane, 3);
+
+ // bottom time axis pane
+ if (showBottomAxis) {
+ const bottomAxisPane = newTimeAxisPane(contentPaneArgs, null, cursor);
+ ui.addPane('bottom-axis-pane', bottomAxisPane);
+ bottomAxisPane.addPainter(newTimeAxisPainter(timeAxis, Side.BOTTOM, bottomTimeZone, tickTimeZone, axisOpts));
+ underlayPane.addPane(newInsetPane(bottomAxisPane, axisInsets), 4, { height: axisPaneHeight, width: null });
+ }
+
+ const updateMillisPerPx = function () {
+ const w = underlayPane.viewport.w - axisInsets.left - axisInsets.right;
+ ui.millisPerPx.value = timeAxis.tSize_MILLIS / w;
+ };
+ underlayPane.viewportChanged.on(updateMillisPerPx);
+ timeAxis.limitsChanged.on(updateMillisPerPx);
+
+ const timelinePane = new TimelinePane(newOverlayLayout(), model, ui);
+ ui.addPane('timeline-pane', timelinePane);
+ timelinePane.addPainter(newBackgroundPainter(bgColor));
+ timelinePane.addPane(underlayPane, true);
+
+ if (selectedIntervalMode === 'single' || selectedIntervalMode === 'single-unmodifiable') {
+ const overlayPane = new Pane(null, false, alwaysTrue);
+ ui.addPane('overlay-pane', overlayPane);
+ overlayPane.addPainter(newTimelineSingleSelectionPainter(timeAxis, selection.selectedInterval, selectedIntervalBorderColor, selectedIntervalFillColor));
+ timelinePane.addPane(newInsetPane(overlayPane, axisInsets, null, false));
+ }
+ else if (selectedIntervalMode === 'range' || selectedIntervalMode === 'range-unmodifiable') {
+ const overlayPane = new Pane(null, false, alwaysTrue);
+ ui.addPane('overlay-pane', overlayPane);
+ overlayPane.addPainter(newTimelineRangeSelectionPainter(timeAxis, selection.selectedInterval, selectedIntervalBorderColor, selectedIntervalFillColor));
+ timelinePane.addPane(newInsetPane(overlayPane, axisInsets, null, false));
+ }
+
+ // Enable double click to center selection on mouse
+
+ if (centerSelectedIntervalOnDoubleClick) {
+ const doubleClick = function (ev: PointerEvent) {
+ if (selectedIntervalMode === 'single') {
+ if (ev.clickCount > 1) {
+ const time_PMILLIS = timeAtPointer_PMILLIS(timeAxis, ev);
+ selection.selectedInterval.setInterval(time_PMILLIS, time_PMILLIS);
+ }
}
+ else if (selectedIntervalMode === 'range') {
+ if (ev.clickCount > 1) {
+ const time_PMILLIS = timeAtPointer_PMILLIS(timeAxis, ev);
+ const offset_PMILLIS = selection.selectedInterval.start_PMILLIS + 0.5 * selection.selectedInterval.duration_MILLIS;
+ selection.selectedInterval.pan(time_PMILLIS - offset_PMILLIS);
+ }
+ }
+ };
+ ui.input.mouseDown.on(doubleClick);
+ }
+
+ timelinePane.dispose.on(function () {
+ // only dispose the ui if we created it (and this manage its lifecycle)
+ if (!outsideManagedUi) {
+ ui.dispose.fire();
}
+ selection.selectedInterval.changed.off(redraw);
+ selection.hoveredEvent.changed.off(redraw);
+ selection.hoveredY.changed.off(redrawCursor);
+ selection.hoveredTime_PMILLIS.changed.off(redrawCursor);
+ selection.selectedEvents.valueAdded.off(redraw);
+ selection.selectedEvents.valueRemoved.off(redraw);
+ underlayPane.viewportChanged.off(updateMillisPerPx);
+ timeAxis.limitsChanged.off(updateMillisPerPx);
+ model.root.maximizedRowGuids.valueAdded.off(updateMaximizedRows);
+ model.root.maximizedRowGuids.valueRemoved.off(updateMaximizedRows);
+ });
+
+ return timelinePane;
+}
- selection.hoveredY.changed.on( redrawCursor );
- selection.hoveredTime_PMILLIS.changed.on( redrawCursor );
- // Scroll Pane and Maximized Row Pane
- //
-
- // setup Pane which either shows timeline content, or only maximized rows
- // able to switch between the two depending on model.root.maximizedRowGuids.isEmpty
-
- // Scroll Pane
-
- var tickTimeZone = ( showTopAxis ? topTimeZone : bottomTimeZone );
- var contentPaneOpts = { selectedIntervalMode: selectedIntervalMode, rowPaneFactoryChooser: rowPaneFactoryChooser, font: font, fgColor: fgColor, rowLabelColor: rowLabelColor, rowLabelBgColor: rowLabelBgColor, groupLabelColor: groupLabelColor, axisLabelColor: axisLabelColor, bgColor: bgColor, rowBgColor: rowBgColor, rowAltBgColor: rowAltBgColor, gridColor: gridColor, gridTickSpacing: tickSpacing, gridTimeZone: tickTimeZone, referenceDate: options.referenceDate, groupLabelInsets: groupLabelInsets, rowLabelInsets: rowLabelInsets, rowLabelPaneWidth: rowLabelPaneWidth, rowSeparatorHeight: rowSeparatorHeight, draggableEdgeWidth: draggableEdgeWidth, snapToDistance: snapToDistance, mouseWheelListener: mouseWheelListener };
- var contentPaneArgs;
-
- if ( showScrollbar ) {
-
- var scrollLayout = newVerticalScrollLayout( );
- var scrollable = new Pane( scrollLayout, false );
- ui.addPane( 'scroll-content-pane', scrollable );
-
- contentPaneArgs = { drawable: drawable, scrollLayout: scrollLayout, timeAxis: timeAxis, model: model, ui: ui, options: contentPaneOpts };
-
- var scrollContentPane = newTimelineContentPane( contentPaneArgs );
- ui.addPane( 'content-pane', scrollContentPane );
-
- scrollable.addPane( scrollContentPane, 0 );
-
- var scrollbar = newVerticalScrollbar( scrollLayout, drawable, scrollbarOptions );
- ui.addPane( 'scrollbar', scrollbar );
-
- var contentPane = new Pane( newColumnLayout( false ), false );
- ui.addPane( 'scroll-outer-pane', contentPane );
-
- contentPane.addPane( scrollbar, 0, { width: scrollbarWidth, ignoreHeight: true } );
- contentPane.addPane( scrollable, 1 );
-
- }
- else {
-
- contentPaneArgs = { drawable: drawable, scrollLayout: null, timeAxis: timeAxis, model: model, ui: ui, options: contentPaneOpts };
- var contentPane = newTimelineContentPane( contentPaneArgs );
- ui.addPane( 'content-pane', contentPane );
-
- }
-
- // Card Pane Switching Logic
-
- var timelineCardPane = new Pane( newCardLayout( ) );
- ui.addPane( 'switch-content-pane', timelineCardPane );
-
- var maximizedContentPane = new Pane( newRowLayout( ) );
- ui.addPane( 'maximize-content-pane', maximizedContentPane );
-
- var insetMaximizedContentPane = newInsetPane( maximizedContentPane, newInsets( 0, scrollbarWidth, 0, 0 ) );
- ui.addPane( 'inset-maximize-content-pane', insetMaximizedContentPane );
-
- var contentActive = model.root.maximizedRowGuids.isEmpty;
- timelineCardPane.addPane( insetMaximizedContentPane, !contentActive );
- timelineCardPane.addPane( contentPane, contentActive );
-
- setupRowContainerPane( contentPaneArgs, maximizedContentPane, model.root.maximizedRowGuids, true, 'maximized' );
-
- var updateMaximizedRows = function( rowGuid : string, rowIndex : number ) {
- var contentActive = model.root.maximizedRowGuids.isEmpty;
- timelineCardPane.setLayoutArg( insetMaximizedContentPane, !contentActive );
- timelineCardPane.setLayoutArg( contentPane, contentActive );
- drawable.redraw( );
- }
-
- model.root.maximizedRowGuids.valueAdded.on( updateMaximizedRows );
- model.root.maximizedRowGuids.valueRemoved.on( updateMaximizedRows );
- // Overlay and Underlay Panes
- //
+function newTimeIntervalMask(timeAxis: TimeAxis1D, interval: TimeIntervalModel, selectedIntervalMode: string): Mask2D {
- var underlayPane = new Pane( newRowLayout( ) );
- ui.addPane( 'underlay-pane', underlayPane );
-
- var axisInsets = newInsets( 0, scrollbarWidth, 0, rowLabelPaneWidth );
-
- // top time axis pane
- var axisOpts = { tickSpacing: tickSpacing, font: font, textColor: axisLabelColor, tickColor: axisLabelColor, labelAlign: axisLabelAlign, referenceDate: options.referenceDate, isFuturePositive : options.isFuturePositive };
- if ( showTopAxis ) {
- var topAxisPane = newTimeAxisPane( contentPaneArgs, null );
- ui.addPane( 'top-axis-pane', topAxisPane );
- topAxisPane.addPainter( newTimeAxisPainter( timeAxis, Side.TOP, topTimeZone, tickTimeZone, axisOpts ) );
- underlayPane.addPane( newInsetPane( topAxisPane, axisInsets ), 0, { height: axisPaneHeight, width: null } );
- }
+ if (selectedIntervalMode === 'range') {
+ return function (viewport: BoundsUnmodifiable, i: number, j: number): boolean {
+ const time_PMILLIS = timeAxis.tAtFrac_PMILLIS(viewport.xFrac(i));
- // pane containing pinned rows specified in TimelineRoot.topPinnedRowGuids
- var topPinnedPane = new Pane( newRowLayout( ) );
- ui.addPane( 'top-pinned-pane', topPinnedPane );
-
- var insetTopPinnedPane = newInsetPane( topPinnedPane, newInsets( 0, scrollbarWidth, 0, 0 ) );
- ui.addPane( 'inset-top-pinned-pane', insetTopPinnedPane );
-
- setupRowContainerPane( contentPaneArgs, topPinnedPane, model.root.topPinnedRowGuids, false, 'toppinned' );
- underlayPane.addPane( insetTopPinnedPane, 1 );
-
- // main pane containing timeline groups and rows
- underlayPane.addPane( timelineCardPane, 2, { height: 'pref-max', width: null } );
-
- // pane containing pinned rows specified in TimelineRoot.bottomPinnedRowGuids
- var bottomPinnedPane = new Pane( newRowLayout( ) );
- ui.addPane( 'bottom-pinned-pane', bottomPinnedPane );
-
- var insetBottomPinnedPane = newInsetPane( bottomPinnedPane, newInsets( 0, scrollbarWidth, 0, 0 ) );
- ui.addPane( 'inset-bottom-pinned-pane', insetBottomPinnedPane );
-
- setupRowContainerPane( contentPaneArgs, bottomPinnedPane, model.root.bottomPinnedRowGuids, false, 'bottompinned' );
- underlayPane.addPane( insetBottomPinnedPane, 3 );
-
- // bottom time axis pane
- if ( showBottomAxis ) {
- var bottomAxisPane = newTimeAxisPane( contentPaneArgs, null );
- ui.addPane( 'bottom-axis-pane', bottomAxisPane );
- bottomAxisPane.addPainter( newTimeAxisPainter( timeAxis, Side.BOTTOM, bottomTimeZone, tickTimeZone, axisOpts ) );
- underlayPane.addPane( newInsetPane( bottomAxisPane, axisInsets ), 4, { height: axisPaneHeight, width: null } );
- }
+ // allow a 10 pixel selection buffer to make it easier to grab ends of the selection
+ const buffer_MILLIS = timeAxis.tSize_MILLIS / viewport.w * 10;
- var updateMillisPerPx = function( ) {
- var w = underlayPane.viewport.w - axisInsets.left - axisInsets.right;
- ui.millisPerPx.value = timeAxis.tSize_MILLIS / w;
+ return interval.overlaps(time_PMILLIS - buffer_MILLIS, time_PMILLIS + buffer_MILLIS);
};
- underlayPane.viewportChanged.on( updateMillisPerPx );
- timeAxis.limitsChanged.on( updateMillisPerPx );
-
- var timelinePane = new TimelinePane( newOverlayLayout( ), model, ui );
- ui.addPane( 'timeline-pane', timelinePane );
- timelinePane.addPainter( newBackgroundPainter( bgColor ) );
- timelinePane.addPane( underlayPane, true );
-
- if ( selectedIntervalMode === 'single' || selectedIntervalMode === 'single-unmodifiable' ) {
- var overlayPane = new Pane( null, false, alwaysTrue );
- ui.addPane( 'overlay-pane', overlayPane );
- overlayPane.addPainter( newTimelineSingleSelectionPainter( timeAxis, selection.selectedInterval, selectedIntervalBorderColor, selectedIntervalFillColor ) );
- timelinePane.addPane( newInsetPane( overlayPane, axisInsets, null, false ) );
- }
- else if ( selectedIntervalMode === 'range' || selectedIntervalMode === 'range-unmodifiable' ) {
- var overlayPane = new Pane( null, false, alwaysTrue );
- ui.addPane( 'overlay-pane', overlayPane );
- overlayPane.addPainter( newTimelineRangeSelectionPainter( timeAxis, selection.selectedInterval, selectedIntervalBorderColor, selectedIntervalFillColor ) );
- timelinePane.addPane( newInsetPane( overlayPane, axisInsets, null, false ) );
+ }
+ else if (selectedIntervalMode === 'single') {
+ return function (viewport: BoundsUnmodifiable, i: number, j: number): boolean {
+ const time_PMILLIS = timeAxis.tAtFrac_PMILLIS(viewport.xFrac(i));
+
+ // allow a 10 pixel selection buffer to make it easier to grab the selection
+ const buffer_MILLIS = timeAxis.tSize_MILLIS / viewport.w * 10;
+
+ return time_PMILLIS < interval.cursor_PMILLIS + buffer_MILLIS && time_PMILLIS > interval.cursor_PMILLIS - buffer_MILLIS;
+ };
+ }
+}
+
+function attachTimeAxisMouseListeners(pane: Pane, axis: Axis1D, args: TimelineContentPaneArguments) {
+ let vGrab: number = null;
+
+ pane.mouseDown.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent) && !hasval(vGrab)) {
+ vGrab = axis.vAtFrac(xFrac(ev));
}
+ });
- // Enable double click to center selection on mouse
-
- if ( centerSelectedIntervalOnDoubleClick ) {
- var doubleClick = function( ev : PointerEvent ) {
- if ( selectedIntervalMode === 'single' ) {
- if ( ev.clickCount > 1 ) {
- var time_PMILLIS = timeAtPointer_PMILLIS( timeAxis, ev );
- selection.selectedInterval.setInterval( time_PMILLIS, time_PMILLIS );
- }
- }
- else if ( selectedIntervalMode === 'range' ) {
- if ( ev.clickCount > 1 ) {
- var time_PMILLIS = timeAtPointer_PMILLIS( timeAxis, ev );
- var offset_PMILLIS = selection.selectedInterval.start_PMILLIS + 0.5*selection.selectedInterval.duration_MILLIS;
- selection.selectedInterval.pan( time_PMILLIS - offset_PMILLIS );
- }
- }
- };
- ui.input.mouseDown.on( doubleClick );
+ pane.mouseMove.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent) && hasval(vGrab)) {
+ axis.pan(vGrab - axis.vAtFrac(xFrac(ev)));
}
-
- timelinePane.dispose.on( function( ) {
- // only dispose the ui if we created it (and this manage its lifecycle)
- if ( !outsideManagedUi ) ui.dispose.fire( );
- selection.selectedInterval.changed.off( redraw );
- selection.hoveredEvent.changed.off( redraw );
- selection.hoveredY.changed.off( redrawCursor );
- selection.hoveredTime_PMILLIS.changed.off( redrawCursor );
- selection.selectedEvents.valueAdded.off( redraw );
- selection.selectedEvents.valueRemoved.off( redraw );
- underlayPane.viewportChanged.off( updateMillisPerPx );
- timeAxis.limitsChanged.off( updateMillisPerPx );
- model.root.maximizedRowGuids.valueAdded.off( updateMaximizedRows );
- model.root.maximizedRowGuids.valueRemoved.off( updateMaximizedRows );
- } );
-
- return timelinePane;
- }
+ });
+ pane.mouseUp.on(function (ev: PointerEvent) {
+ vGrab = null;
+ });
+ pane.mouseWheel.on(args.options.mouseWheelListener);
+}
- function newTimeIntervalMask( timeAxis : TimeAxis1D, interval : TimeIntervalModel, selectedIntervalMode : string ) : Mask2D {
-
- if ( selectedIntervalMode === 'range' ) {
- return function( viewport : BoundsUnmodifiable, i : number, j : number ) : boolean {
- var time_PMILLIS = timeAxis.tAtFrac_PMILLIS( viewport.xFrac( i ) );
-
- // allow a 10 pixel selection buffer to make it easier to grab ends of the selection
- var buffer_MILLIS = timeAxis.tSize_MILLIS / viewport.w * 10;
-
- return interval.overlaps( time_PMILLIS - buffer_MILLIS, time_PMILLIS + buffer_MILLIS );
- };
+function newTimeAxisPane(args: TimelineContentPaneArguments, row: TimelineRowModel, cursor: string): Pane {
+ const timeAxis = args.timeAxis;
+ const ui = args.ui;
+ const draggableEdgeWidth = args.options.draggableEdgeWidth;
+ const scrollLayout = args.scrollLayout;
+ const drawable = args.drawable;
+ const selectedIntervalMode = args.options.selectedIntervalMode;
+
+ const input = ui.input;
+
+ const axisPane = new Pane(newOverlayLayout());
+ if (scrollLayout) {
+ attachTimelineVerticalScrollMouseListeners(axisPane, scrollLayout, drawable);
+ }
+ attachTimeAxisMouseListeners(axisPane, timeAxis, args);
+
+ const onMouseMove = function (ev: PointerEvent) {
+ const time_PMILLIS = timeAxis.tAtFrac_PMILLIS(xFrac(ev));
+ input.mouseMove.fire(ev);
+ input.timeHover.fire(time_PMILLIS, ev);
+ if (row) {
+ input.rowHover.fire(row, ev);
}
- else if ( selectedIntervalMode === 'single' ) {
- return function( viewport : BoundsUnmodifiable, i : number, j : number ) : boolean {
- var time_PMILLIS = timeAxis.tAtFrac_PMILLIS( viewport.xFrac( i ) );
-
- // allow a 10 pixel selection buffer to make it easier to grab the selection
- var buffer_MILLIS = timeAxis.tSize_MILLIS / viewport.w * 10;
-
- return time_PMILLIS < interval.cursor_PMILLIS + buffer_MILLIS && time_PMILLIS > interval.cursor_PMILLIS - buffer_MILLIS;
- };
+ };
+ axisPane.mouseMove.on(onMouseMove);
+
+ const onMouseExit = function (ev: PointerEvent) {
+ input.mouseExit.fire(ev);
+ input.timeHover.fire(null, ev);
+ if (row) {
+ input.rowHover.fire(null, ev);
}
+ };
+ axisPane.mouseExit.on(onMouseExit);
+
+ const onMouseDown = function (ev: PointerEvent) {
+ input.mouseDown.fire(ev);
+ };
+ axisPane.mouseDown.on(onMouseDown);
+
+ const onMouseUp = function (ev: PointerEvent) {
+ input.mouseUp.fire(ev);
+ };
+ axisPane.mouseUp.on(onMouseUp);
+
+ const onContextMenu = function (ev: PointerEvent) {
+ input.contextMenu.fire(ev);
+ };
+ axisPane.contextMenu.on(onContextMenu);
+
+ if (cursor) {
+ axisPane.mouseCursor = cursor;
}
- function attachTimeAxisMouseListeners( pane : Pane, axis : Axis1D, args : TimelineContentPaneArguments ) {
- var vGrab : number = null;
+ if (selectedIntervalMode === 'single' || selectedIntervalMode === 'range') {
+ const selection = ui.selection;
+ const selectedIntervalPane = new Pane(null, true, newTimeIntervalMask(timeAxis, selection.selectedInterval, selectedIntervalMode));
+ attachTimeSelectionMouseListeners(selectedIntervalPane, timeAxis, selection.selectedInterval, input, draggableEdgeWidth, selectedIntervalMode, args.options.mouseWheelListener);
+ axisPane.addPane(selectedIntervalPane, false);
+
+ selectedIntervalPane.mouseMove.on(onMouseMove);
+ selectedIntervalPane.mouseExit.on(onMouseExit);
+ selectedIntervalPane.mouseDown.on(onMouseDown);
+ selectedIntervalPane.mouseUp.on(onMouseUp);
+ selectedIntervalPane.contextMenu.on(onContextMenu);
+ }
- pane.mouseDown.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) && !hasval( vGrab ) ) {
- vGrab = axis.vAtFrac( xFrac( ev ) );
- }
- } );
+ // Dispose
+ //
- pane.mouseMove.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) && hasval( vGrab ) ) {
- axis.pan( vGrab - axis.vAtFrac( xFrac( ev ) ) );
- }
- } );
+ // mouse listeners are disposed of automatically by Pane
- pane.mouseUp.on( function( ev : PointerEvent ) {
- vGrab = null;
- } );
+ return axisPane;
+}
- pane.mouseWheel.on( args.options.mouseWheelListener );
- }
+function timeAtPointer_PMILLIS(timeAxis: TimeAxis1D, ev: PointerEvent): number {
+ return timeAxis.tAtFrac_PMILLIS(ev.paneViewport.xFrac(ev.i));
+}
- function newTimeAxisPane( args : TimelineContentPaneArguments, row : TimelineRowModel ) : Pane {
- var timeAxis = args.timeAxis;
- var ui = args.ui;
- var draggableEdgeWidth = args.options.draggableEdgeWidth;
- var scrollLayout = args.scrollLayout;
- var drawable = args.drawable;
- var selectedIntervalMode = args.options.selectedIntervalMode;
-
- var input = ui.input;
-
- var axisPane = new Pane( newOverlayLayout( ) );
- if ( scrollLayout ) attachTimelineVerticalScrollMouseListeners( axisPane, scrollLayout, drawable );
- attachTimeAxisMouseListeners( axisPane, timeAxis, args );
-
- var onMouseMove = function( ev : PointerEvent ) {
- var time_PMILLIS = timeAxis.tAtFrac_PMILLIS( xFrac( ev ) );
- input.mouseMove.fire( ev );
- input.timeHover.fire( time_PMILLIS, ev );
- if ( row ) input.rowHover.fire( row, ev );
- };
- axisPane.mouseMove.on( onMouseMove );
- var onMouseExit = function( ev : PointerEvent ) {
- input.mouseExit.fire( ev );
- input.timeHover.fire( null, ev );
- if ( row ) input.rowHover.fire( null, ev );
- };
- axisPane.mouseExit.on( onMouseExit );
+function attachTimeSelectionMouseListeners(pane: Pane,
+ timeAxis: TimeAxis1D,
+ interval: TimeIntervalModel,
+ input: TimelineInput,
+ draggableEdgeWidth: number,
+ selectedIntervalMode: string,
+ mouseWheelListener: (PointerEvent: PointerEvent) => void) {
- var onMouseDown = function( ev : PointerEvent ) {
- input.mouseDown.fire( ev );
- };
- axisPane.mouseDown.on( onMouseDown );
+ if (selectedIntervalMode === 'single') {
- var onMouseUp = function( ev : PointerEvent ) {
- input.mouseUp.fire( ev );
- };
- axisPane.mouseUp.on( onMouseUp );
-
- var onContextMenu = function( ev : PointerEvent ) {
- input.contextMenu.fire( ev );
+ const chooseDragMode = function (ev: PointerEvent): string {
+ return 'center';
};
- axisPane.contextMenu.on( onContextMenu );
-
- if ( selectedIntervalMode === 'single' || selectedIntervalMode === 'range' ) {
- var selection = ui.selection;
- var selectedIntervalPane = new Pane( null, true, newTimeIntervalMask( timeAxis, selection.selectedInterval, selectedIntervalMode ) );
- attachTimeSelectionMouseListeners( selectedIntervalPane, timeAxis, selection.selectedInterval, input, draggableEdgeWidth, selectedIntervalMode );
- axisPane.addPane( selectedIntervalPane, false );
-
- selectedIntervalPane.mouseMove.on( onMouseMove );
- selectedIntervalPane.mouseExit.on( onMouseExit );
- selectedIntervalPane.mouseDown.on( onMouseDown );
- selectedIntervalPane.mouseUp.on( onMouseUp );
- selectedIntervalPane.contextMenu.on( onContextMenu );
- }
-
- // Dispose
- //
-
- // mouse listeners are disposed of automatically by Pane
- return axisPane;
- }
+ attachTimeIntervalSelectionMouseListeners(pane, timeAxis, interval, input, draggableEdgeWidth, selectedIntervalMode, chooseDragMode, mouseWheelListener);
- function timeAtPointer_PMILLIS( timeAxis : TimeAxis1D, ev : PointerEvent ) : number {
- return timeAxis.tAtFrac_PMILLIS( ev.paneViewport.xFrac( ev.i ) );
}
-
-
- function attachTimeSelectionMouseListeners( pane : Pane,
- timeAxis : TimeAxis1D,
- interval : TimeIntervalModel,
- input : TimelineInput,
- draggableEdgeWidth : number,
- selectedIntervalMode : string ) {
-
- if ( selectedIntervalMode === 'single' ) {
-
- var chooseDragMode = function chooseDragMode( ev : PointerEvent ) : string {
+ else if (selectedIntervalMode === 'range') {
+
+ // Edges are draggable when interval is at least this wide
+ const minIntervalWidthForEdgeDraggability = 3 * draggableEdgeWidth;
+
+ // When dragging an edge, the interval cannot be made narrower than this
+ //
+ // Needs to be greater than minIntervalWidthForEdgeDraggability -- by enough to
+ // cover floating-point precision loss -- so a user can't accidentally make
+ // the interval so narrow that it can't easily be widened again.
+ //
+ const minIntervalWidthWhenDraggingEdge = minIntervalWidthForEdgeDraggability + 1;
+
+ const chooseDragMode = function (ev: PointerEvent): string {
+ const intervalWidth = (interval.duration_MILLIS) * ev.paneViewport.w / timeAxis.vSize;
+ if (intervalWidth < minIntervalWidthForEdgeDraggability) {
+ // If interval isn't very wide, don't try to allow edge dragging
return 'center';
}
-
- attachTimeIntervalSelectionMouseListeners( pane, timeAxis, interval, input, draggableEdgeWidth, selectedIntervalMode, chooseDragMode );
-
- }
- else if ( selectedIntervalMode === 'range' ) {
-
- // Edges are draggable when interval is at least this wide
- var minIntervalWidthForEdgeDraggability = 3 * draggableEdgeWidth;
-
- // When dragging an edge, the interval cannot be made narrower than this
- //
- // Needs to be greater than minIntervalWidthForEdgeDraggability -- by enough to
- // cover floating-point precision loss -- so a user can't accidentally make
- // the interval so narrow that it can't easily be widened again.
- //
- var minIntervalWidthWhenDraggingEdge = minIntervalWidthForEdgeDraggability + 1;
-
- var chooseDragMode = function chooseDragMode( ev : PointerEvent ) : string {
- var intervalWidth = ( interval.duration_MILLIS ) * ev.paneViewport.w / timeAxis.vSize;
- if ( intervalWidth < minIntervalWidthForEdgeDraggability ) {
- // If interval isn't very wide, don't try to allow edge dragging
+ else {
+ const time_PMILLIS = timeAtPointer_PMILLIS(timeAxis, ev);
+ const mouseOffset = (time_PMILLIS - interval.start_PMILLIS) * ev.paneViewport.w / timeAxis.vSize;
+ if (mouseOffset < draggableEdgeWidth) {
+ // If mouse is near the left edge, drag the interval's start-time
+ return 'start';
+ }
+ else if (mouseOffset < intervalWidth - draggableEdgeWidth) {
+ // If mouse is in the center, drag the whole interval
return 'center';
}
else {
- var time_PMILLIS = timeAtPointer_PMILLIS( timeAxis, ev );
- var mouseOffset = ( time_PMILLIS - interval.start_PMILLIS ) * ev.paneViewport.w / timeAxis.vSize;
- if ( mouseOffset < draggableEdgeWidth ) {
- // If mouse is near the left edge, drag the interval's start-time
- return 'start';
- }
- else if ( mouseOffset < intervalWidth - draggableEdgeWidth ) {
- // If mouse is in the center, drag the whole interval
- return 'center';
- }
- else {
- // If mouse is near the right edge, drag the interval's end-time
- return 'end';
- }
+ // If mouse is near the right edge, drag the interval's end-time
+ return 'end';
}
- };
-
- attachTimeIntervalSelectionMouseListeners( pane, timeAxis, interval, input, draggableEdgeWidth, selectedIntervalMode, chooseDragMode );
- }
+ }
+ };
+
+ attachTimeIntervalSelectionMouseListeners(pane, timeAxis, interval, input, draggableEdgeWidth, selectedIntervalMode, chooseDragMode, mouseWheelListener);
}
-
- function attachTimeIntervalSelectionMouseListeners( pane : Pane,
- timeAxis : TimeAxis1D,
- interval : TimeIntervalModel,
- input : TimelineInput,
- draggableEdgeWidth : number,
- selectedIntervalMode : string,
- chooseDragMode : ( ev : PointerEvent ) => string ) {
-
- // see comments in attachTimeSelectionMouseListeners( ... )
- var minIntervalWidthForEdgeDraggability = 3 * draggableEdgeWidth;
- var minIntervalWidthWhenDraggingEdge = minIntervalWidthForEdgeDraggability + 1;
-
- // Hook up input notifications
- //
+}
- pane.mouseWheel.on( function( ev : PointerEvent ) {
- var zoomFactor = Math.pow( axisZoomStep, ev.wheelSteps );
- timeAxis.zoom( zoomFactor, timeAxis.vAtFrac( xFrac( ev ) ) );
- } );
+function attachTimeIntervalSelectionMouseListeners(pane: Pane,
+ timeAxis: TimeAxis1D,
+ interval: TimeIntervalModel,
+ input: TimelineInput,
+ draggableEdgeWidth: number,
+ selectedIntervalMode: string,
+ chooseDragMode: (ev: PointerEvent) => string,
+ mouseWheelListener: (PointerEvent: PointerEvent) => void) {
- pane.contextMenu.on( function( ev : PointerEvent ) {
- input.contextMenu.fire( ev );
- } );
+ // see comments in attachTimeSelectionMouseListeners( ... )
+ const minIntervalWidthForEdgeDraggability = 3 * draggableEdgeWidth;
+ const minIntervalWidthWhenDraggingEdge = minIntervalWidthForEdgeDraggability + 1;
+ // Hook up input notifications
+ //
+ pane.mouseWheel.on(mouseWheelListener);
+ pane.contextMenu.on(function (ev: PointerEvent) {
+ input.contextMenu.fire(ev);
+ });
- // Begin interval-drag
- //
- var dragMode : string = null;
- var dragOffset_MILLIS : number = null;
+ // Begin interval-drag
+ //
- pane.mouseMove.on( function( ev : PointerEvent ) {
- if ( !dragMode ) {
- var mouseCursors = { 'center': 'move', 'start': 'w-resize', 'end': 'e-resize' };
- pane.mouseCursor = mouseCursors[ chooseDragMode( ev ) ];
- }
- } );
+ let dragMode: string = null;
+ let dragOffset_MILLIS: number = null;
- pane.mouseDown.on( function( ev : PointerEvent ) {
- dragMode = isLeftMouseDown( ev.mouseEvent ) ? chooseDragMode( ev ) : null;
- if ( !hasval( dragMode ) ) {
- dragOffset_MILLIS = null;
- }
- } );
+ pane.mouseMove.on(function (ev: PointerEvent) {
+ if (!dragMode) {
+ const mouseCursors = { 'center': 'move', 'start': 'w-resize', 'end': 'e-resize' };
+ pane.mouseCursor = mouseCursors[chooseDragMode(ev)];
+ }
+ });
+ pane.mouseDown.on(function (ev: PointerEvent) {
+ dragMode = isLeftMouseDown(ev.mouseEvent) ? chooseDragMode(ev) : null;
+ if (!hasval(dragMode)) {
+ dragOffset_MILLIS = null;
+ }
+ });
- // Compute (and remember) the pointer time, for use by the drag listeners below
- //
- var dragPointer_PMILLIS : number = null;
+ // Compute (and remember) the pointer time, for use by the drag listeners below
+ //
- var updateDragPointer = function( ev : PointerEvent ) {
- if ( hasval( dragMode ) ) {
- dragPointer_PMILLIS = timeAtPointer_PMILLIS( timeAxis, ev );
- }
- };
- pane.mouseDown.on( updateDragPointer );
- pane.mouseMove.on( updateDragPointer );
+ let dragPointer_PMILLIS: number = null;
+ const updateDragPointer = function (ev: PointerEvent) {
+ if (hasval(dragMode)) {
+ dragPointer_PMILLIS = timeAtPointer_PMILLIS(timeAxis, ev);
+ }
+ };
+ pane.mouseDown.on(updateDragPointer);
+ pane.mouseMove.on(updateDragPointer);
- // Dragging interval-center
- //
- var grabCenter = function( ) {
- if ( dragMode === 'center' ) {
- dragOffset_MILLIS = dragPointer_PMILLIS - interval.start_PMILLIS;
- }
- };
- pane.mouseDown.on( grabCenter );
+ // Dragging interval-center
+ //
- var dragCenter = function( ) {
- if ( dragMode === 'center' ) {
- var newStart_PMILLIS = ( dragPointer_PMILLIS - dragOffset_MILLIS );
- var newEnd_PMILLIS = interval.end_PMILLIS + ( newStart_PMILLIS - interval.start_PMILLIS );
- interval.setInterval( newStart_PMILLIS, newEnd_PMILLIS );
- }
- };
- pane.mouseMove.on( dragCenter );
+ const grabCenter = function () {
+ if (dragMode === 'center') {
+ dragOffset_MILLIS = dragPointer_PMILLIS - interval.start_PMILLIS;
+ }
+ };
+ pane.mouseDown.on(grabCenter);
+
+ const dragCenter = function () {
+ if (dragMode === 'center') {
+ const newStart_PMILLIS = (dragPointer_PMILLIS - dragOffset_MILLIS);
+ const newEnd_PMILLIS = interval.end_PMILLIS + (newStart_PMILLIS - interval.start_PMILLIS);
+ interval.setInterval(newStart_PMILLIS, newEnd_PMILLIS);
+ }
+ };
+ pane.mouseMove.on(dragCenter);
- // Dragging interval-start
- //
+ // Dragging interval-start
+ //
- var grabStart = function( ) {
- if ( dragMode === 'start' ) {
- dragOffset_MILLIS = dragPointer_PMILLIS - interval.start_PMILLIS;
- }
- };
- pane.mouseDown.on( grabStart );
+ const grabStart = function () {
+ if (dragMode === 'start') {
+ dragOffset_MILLIS = dragPointer_PMILLIS - interval.start_PMILLIS;
+ }
+ };
+ pane.mouseDown.on(grabStart);
+
+ const dragStart = function () {
+ if (dragMode === 'start') {
+ const wMin_MILLIS = minIntervalWidthWhenDraggingEdge * timeAxis.vSize / pane.viewport.w;
+ const newStart_PMILLIS = dragPointer_PMILLIS - dragOffset_MILLIS;
+ interval.start_PMILLIS = Math.min(interval.end_PMILLIS - wMin_MILLIS, newStart_PMILLIS);
+ }
+ };
+ pane.mouseMove.on(dragStart);
- var dragStart = function( ) {
- if ( dragMode === 'start' ) {
- var wMin_MILLIS = minIntervalWidthWhenDraggingEdge * timeAxis.vSize / pane.viewport.w;
- var newStart_PMILLIS = dragPointer_PMILLIS - dragOffset_MILLIS;
- interval.start_PMILLIS = Math.min( interval.end_PMILLIS - wMin_MILLIS, newStart_PMILLIS );
- }
- };
- pane.mouseMove.on( dragStart );
+ // Dragging interval-end
+ //
- // Dragging interval-end
- //
+ const grabEnd = function () {
+ if (dragMode === 'end') {
+ dragOffset_MILLIS = dragPointer_PMILLIS - interval.end_PMILLIS;
+ }
+ };
+ pane.mouseDown.on(grabEnd);
+
+ const dragEnd = function () {
+ if (dragMode === 'end') {
+ const wMin_MILLIS = minIntervalWidthWhenDraggingEdge * timeAxis.vSize / pane.viewport.w;
+ const newEnd_PMILLIS = dragPointer_PMILLIS - dragOffset_MILLIS;
+ interval.end_PMILLIS = Math.max(interval.start_PMILLIS + wMin_MILLIS, newEnd_PMILLIS);
+ interval.cursor_PMILLIS = interval.end_PMILLIS;
+ }
+ };
+ pane.mouseMove.on(dragEnd);
- var grabEnd = function( ) {
- if ( dragMode === 'end' ) {
- dragOffset_MILLIS = dragPointer_PMILLIS - interval.end_PMILLIS;
- }
- };
- pane.mouseDown.on( grabEnd );
-
- var dragEnd = function( ) {
- if ( dragMode === 'end' ) {
- var wMin_MILLIS = minIntervalWidthWhenDraggingEdge * timeAxis.vSize / pane.viewport.w;
- var newEnd_PMILLIS = dragPointer_PMILLIS - dragOffset_MILLIS;
- interval.end_PMILLIS = Math.max( interval.start_PMILLIS + wMin_MILLIS, newEnd_PMILLIS );
- interval.cursor_PMILLIS = interval.end_PMILLIS;
- }
- };
- pane.mouseMove.on( dragEnd );
+ // Finish interval-drag
+ //
- // Finish interval-drag
- //
+ pane.mouseUp.on(function (ev: PointerEvent) {
+ dragOffset_MILLIS = null;
+ dragPointer_PMILLIS = null;
+ dragMode = null;
+ });
+}
+
+export function newTimelineSingleSelectionPainter(timeAxis: TimeAxis1D, interval: TimeIntervalModel, borderColor: Color, fillColor: Color): Painter {
+
+ const program = new Program(xyFrac_VERTSHADER, solid_FRAGSHADER);
+ const a_XyFrac = new Attribute(program, 'a_XyFrac');
+ const u_Color = new UniformColor(program, 'u_Color');
+
+ // holds vertices for fill and border
+ const coords = new Float32Array(12 + 8);
+ const coordsBuffer = newDynamicBuffer();
+
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ if (hasval(interval.cursor_PMILLIS)) {
+
+ const fracSelection = timeAxis.tFrac(interval.cursor_PMILLIS);
+ const fracWidth = 1 / viewport.w;
+ const fracHeight = 1 / viewport.h;
+ const thickWidth = 3 / viewport.w;
+ const highlightWidth = 7 / viewport.w;
+ let index = 0;
+
+ // fill vertices
+ coords[index++] = fracSelection - highlightWidth;
+ coords[index++] = 1;
+ coords[index++] = fracSelection + highlightWidth;
+ coords[index++] = 1;
+ coords[index++] = fracSelection - highlightWidth;
+ coords[index++] = 0;
+ coords[index++] = fracSelection + highlightWidth;
+ coords[index++] = 0;
+
+ // selection vertices
+ index = putQuadXys(coords, index, fracSelection - thickWidth / 2, fracSelection + thickWidth / 2, 1, 0 + fracHeight); // selection
+
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ program.use(gl);
+ coordsBuffer.setData(coords);
+ a_XyFrac.setDataAndEnable(gl, coordsBuffer, 2, GL.FLOAT);
+
+ u_Color.setData(gl, fillColor);
+ gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
+
+ u_Color.setData(gl, borderColor);
+ gl.drawArrays(GL.TRIANGLES, 4, 6);
+
+ a_XyFrac.disable(gl);
+ program.endUse(gl);
- pane.mouseUp.on( function( ev : PointerEvent ) {
- dragOffset_MILLIS = null;
- dragPointer_PMILLIS = null;
- dragMode = null;
- } );
- }
-
- function newTimelineSingleSelectionPainter( timeAxis : TimeAxis1D, interval : TimeIntervalModel, borderColor : Color, fillColor : Color ) : Painter {
-
- var program = new Program( xyFrac_VERTSHADER, solid_FRAGSHADER );
- var a_XyFrac = new Attribute( program, 'a_XyFrac' );
- var u_Color = new UniformColor( program, 'u_Color' );
-
- // holds vertices for fill and border
- var coords = new Float32Array( 12 + 8 );
- var coordsBuffer = newDynamicBuffer( );
-
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- if ( hasval( interval.cursor_PMILLIS ) ) {
-
- var fracSelection = timeAxis.tFrac( interval.cursor_PMILLIS );
- var fracWidth = 1 / viewport.w;
- var fracHeight = 1 / viewport.h;
- var thickWidth = 3 / viewport.w;
- var highlightWidth = 7 / viewport.w;
- var index = 0;
-
- // fill vertices
- coords[ index++ ] = fracSelection - highlightWidth;
- coords[ index++ ] = 1;
- coords[ index++ ] = fracSelection + highlightWidth;
- coords[ index++ ] = 1;
- coords[ index++ ] = fracSelection - highlightWidth;
- coords[ index++ ] = 0;
- coords[ index++ ] = fracSelection + highlightWidth;
- coords[ index++ ] = 0;
-
- // selection vertices
- index = putQuadXys( coords, index, fracSelection-thickWidth/2, fracSelection+thickWidth/2, 1, 0+fracHeight ); // selection
-
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
-
- program.use( gl );
- coordsBuffer.setData( coords );
- a_XyFrac.setDataAndEnable( gl, coordsBuffer, 2, GL.FLOAT );
-
- u_Color.setData( gl, fillColor );
- gl.drawArrays( GL.TRIANGLE_STRIP, 0, 4 );
-
- u_Color.setData( gl, borderColor );
- gl.drawArrays( GL.TRIANGLES, 4, 6 );
-
- a_XyFrac.disable( gl );
- program.endUse( gl );
-
-
- }
- };
- }
- function newTimelineRangeSelectionPainter( timeAxis : TimeAxis1D, interval : TimeIntervalModel, borderColor : Color, fillColor : Color ) : Painter {
-
- var program = new Program( xyFrac_VERTSHADER, solid_FRAGSHADER );
- var a_XyFrac = new Attribute( program, 'a_XyFrac' );
- var u_Color = new UniformColor( program, 'u_Color' );
-
- // holds vertices for fill and border
- var coords = new Float32Array( 12 + 8 + 48 );
- var coordsBuffer = newDynamicBuffer( );
-
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
- if ( hasval( interval.start_PMILLIS ) && hasval( interval.end_PMILLIS ) ) {
-
- var fracStart = timeAxis.tFrac( interval.start_PMILLIS );
- var fracEnd = timeAxis.tFrac( interval.end_PMILLIS );
- var fracSelection = timeAxis.tFrac( interval.cursor_PMILLIS );
- var fracWidth = 1 / viewport.w;
- var fracHeight = 1 / viewport.h;
- var thickWidth = 3 / viewport.w;
- var index = 0;
-
- // fill vertices
- coords[ index++ ] = fracStart;
- coords[ index++ ] = 1;
- coords[ index++ ] = fracEnd;
- coords[ index++ ] = 1;
- coords[ index++ ] = fracStart;
- coords[ index++ ] = 0;
- coords[ index++ ] = fracEnd;
- coords[ index++ ] = 0;
-
- // border vertices
- index = putQuadXys( coords, index, fracStart, fracEnd-fracWidth, +1, +1-fracHeight ); // top
- index = putQuadXys( coords, index, fracStart+fracWidth, fracEnd, 0+fracHeight, 0 ); // bottom
- index = putQuadXys( coords, index, fracStart, fracStart+fracWidth, 1-fracHeight, 0 ); // left
- index = putQuadXys( coords, index, fracEnd-fracWidth, fracEnd, 1, 0+fracHeight ); // right
-
- // selection vertices
- index = putQuadXys( coords, index, fracSelection-thickWidth, fracSelection, 1, 0+fracHeight ); // selection
-
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
-
- program.use( gl );
- coordsBuffer.setData( coords );
- a_XyFrac.setDataAndEnable( gl, coordsBuffer, 2, GL.FLOAT );
-
- u_Color.setData( gl, fillColor );
- gl.drawArrays( GL.TRIANGLE_STRIP, 0, 4 );
-
- u_Color.setData( gl, borderColor );
- gl.drawArrays( GL.TRIANGLES, 4, 30 );
-
- a_XyFrac.disable( gl );
- program.endUse( gl );
-
-
- }
- };
- }
-
- function newGroupCollapseExpandArrowPainter( group : TimelineGroupModel ) {
-
- var program = new Program( xyFrac_VERTSHADER, solid_FRAGSHADER );
- var a_XyFrac = new Attribute( program, 'a_XyFrac' );
- var u_Color = new UniformColor( program, 'u_Color' );
-
- // holds vertices for triangle
- var coords = new Float32Array( 6 );
- var coordsBuffer = newDynamicBuffer( );
-
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
-
- var sizeFracX = 0.5;
- var sizeX = sizeFracX * viewport.w;
- var sizeY = sizeX * Math.sqrt(3)/2;
- var sizeFracY = sizeY / viewport.h;
-
- var bufferFracX = 0.05;
- var bufferSize = bufferFracX * viewport.w;
- var bufferFracY = bufferSize / viewport.h;
-
- var centerFracX = 0.5;
- var centerFracY = bufferFracY + sizeFracY/2;
-
- if ( group.collapsed ) {
-
- sizeFracX = sizeY / viewport.w;
- sizeFracY = sizeX / viewport.h;
-
- var fracStartX = centerFracX - sizeFracX / 2;
- var fracEndX = centerFracX + sizeFracX / 2;
-
- var fracStartY = 1 - ( centerFracY - sizeFracY / 2 ) ;
- var fracEndY = 1 - ( centerFracY + sizeFracY / 2 ) ;
-
- var index = 0;
- coords[ index++ ] = fracStartX;
- coords[ index++ ] = fracStartY;
- coords[ index++ ] = fracEndX;
- coords[ index++ ] = ( fracStartY + fracEndY ) / 2;
- coords[ index++ ] = fracStartX;
- coords[ index++ ] = fracEndY;
- } else {
- var fracStartX = centerFracX - sizeFracX / 2;
- var fracEndX = centerFracX + sizeFracX / 2;
-
- var fracStartY = 1 - ( centerFracY - sizeFracY / 2 ) ;
- var fracEndY = 1 - ( centerFracY + sizeFracY / 2 ) ;
-
- var index = 0;
- coords[ index++ ] = fracStartX;
- coords[ index++ ] = fracStartY;
- coords[ index++ ] = fracEndX;
- coords[ index++ ] = fracStartY;
- coords[ index++ ] = ( fracStartX + fracEndX ) / 2;
- coords[ index++ ] = fracEndY;
- }
-
- program.use( gl );
- coordsBuffer.setData( coords );
- a_XyFrac.setDataAndEnable( gl, coordsBuffer, 2, GL.FLOAT );
-
- u_Color.setData( gl, white );
- gl.drawArrays( GL.TRIANGLES, 0, 3 );
-
- a_XyFrac.disable( gl );
- program.endUse( gl );
}
- }
+ };
+}
- interface TimelineContentPaneOptions {
- selectedIntervalMode : string;
- rowPaneFactoryChooser : TimelineRowPaneFactoryChooser;
-
- font : string;
- fgColor : Color;
- rowLabelColor : Color;
- rowLabelBgColor : Color;
- groupLabelColor : Color;
- axisLabelColor : Color;
- bgColor : Color;
- rowBgColor : Color;
- rowAltBgColor : Color;
- gridColor : Color;
-
- gridTimeZone : string;
- gridTickSpacing : number;
- referenceDate : string;
-
- groupLabelInsets : Insets;
- rowLabelInsets : Insets;
- rowLabelPaneWidth : number;
- rowSeparatorHeight : number;
-
- draggableEdgeWidth : number;
- snapToDistance : number;
-
- mouseWheelListener : ( PointerEvent ) => void;
- }
-
- interface TimelineContentPaneArguments {
- drawable : Drawable;
- scrollLayout : VerticalScrollLayout;
- timeAxis : TimeAxis1D;
- model : TimelineModel;
- ui : TimelineUi;
- options : TimelineContentPaneOptions;
- }
+function newTimelineRangeSelectionPainter(timeAxis: TimeAxis1D, interval: TimeIntervalModel, borderColor: Color, fillColor: Color): Painter {
- function newTimelineContentPane( args : TimelineContentPaneArguments ) : Pane {
- var drawable = args.drawable;
- var scrollLayout = args.scrollLayout;
- var timeAxis = args.timeAxis;
- var model = args.model;
- var ui = args.ui;
- var options = args.options;
-
- var root = model.root;
-
- var selectedIntervalMode = options.selectedIntervalMode;
- var rowPaneFactoryChooser = options.rowPaneFactoryChooser;
-
- var font = options.font;
- var fgColor = options.fgColor;
- var rowLabelColor = options.rowLabelColor;
- var groupLabelColor = options.groupLabelColor;
- var axisLabelColor = options.axisLabelColor;
- var bgColor = options.bgColor;
- var rowBgColor = options.rowBgColor;
- var rowAltBgColor = options.rowAltBgColor;
- var gridColor = options.gridColor;
-
- var gridTimeZone = options.gridTimeZone;
- var gridTickSpacing = options.gridTickSpacing;
-
- var groupLabelInsets = options.groupLabelInsets;
- var rowLabelInsets = options.rowLabelInsets;
- var rowLabelPaneWidth = options.rowLabelPaneWidth;
- var rowSeparatorHeight = options.rowSeparatorHeight;
-
- var draggableEdgeWidth = options.draggableEdgeWidth;
- var snapToDistance = options.snapToDistance;
-
- var textureRenderer = new TextureRenderer( );
- var createGroupLabelTexture = createTextTextureFactory( font );
- var createRowLabelTexture = createTextTextureFactory( font );
-
- // Group panes
- //
+ const program = new Program(xyFrac_VERTSHADER, solid_FRAGSHADER);
+ const a_XyFrac = new Attribute(program, 'a_XyFrac');
+ const u_Color = new UniformColor(program, 'u_Color');
+
+ // holds vertices for fill and border
+ const coords = new Float32Array(12 + 8 + 48);
+ const coordsBuffer = newDynamicBuffer();
+
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+ if (hasval(interval.start_PMILLIS) && hasval(interval.end_PMILLIS)) {
+
+ const fracStart = timeAxis.tFrac(interval.start_PMILLIS);
+ const fracEnd = timeAxis.tFrac(interval.end_PMILLIS);
+ const fracSelection = timeAxis.tFrac(interval.cursor_PMILLIS);
+ const fracWidth = 1 / viewport.w;
+ const fracHeight = 1 / viewport.h;
+ const thickWidth = 3 / viewport.w;
+ let index = 0;
+
+ // fill vertices
+ coords[index++] = fracStart;
+ coords[index++] = 1;
+ coords[index++] = fracEnd;
+ coords[index++] = 1;
+ coords[index++] = fracStart;
+ coords[index++] = 0;
+ coords[index++] = fracEnd;
+ coords[index++] = 0;
+
+ // border vertices
+ index = putQuadXys(coords, index, fracStart, fracEnd - fracWidth, +1, +1 - fracHeight); // top
+ index = putQuadXys(coords, index, fracStart + fracWidth, fracEnd, 0 + fracHeight, 0); // bottom
+ index = putQuadXys(coords, index, fracStart, fracStart + fracWidth, 1 - fracHeight, 0); // left
+ index = putQuadXys(coords, index, fracEnd - fracWidth, fracEnd, 1, 0 + fracHeight); // right
+
+ // selection vertices
+ index = putQuadXys(coords, index, fracSelection - thickWidth, fracSelection, 1, 0 + fracHeight); // selection
- var timelineContentPane = new Pane( newRowLayout( ) );
-
- var groupHeaderPanes : StringMap = {};
- var groupContentPanes : StringMap = {};
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
- var addGroup = function( groupGuid : string, groupIndex : number ) {
- var group = model.group( groupGuid );
+ program.use(gl);
+ coordsBuffer.setData(coords);
+ a_XyFrac.setDataAndEnable(gl, coordsBuffer, 2, GL.FLOAT);
- var groupLabel = new Label( group.label, font, groupLabelColor );
- var groupLabelPane = new Pane( { updatePrefSize: fitToLabel( groupLabel ) }, false );
- groupLabelPane.addPainter( newLabelPainter( groupLabel, 0, 1, 0, 1 ) );
+ u_Color.setData(gl, fillColor);
+ gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
+
+ u_Color.setData(gl, borderColor);
+ gl.drawArrays(GL.TRIANGLES, 4, 30);
+
+ a_XyFrac.disable(gl);
+ program.endUse(gl);
+
+
+ }
+ };
+}
+
+function newGroupCollapseExpandArrowPainter(group: TimelineGroupModel) {
+
+ const program = new Program(xyFrac_VERTSHADER, solid_FRAGSHADER);
+ const a_XyFrac = new Attribute(program, 'a_XyFrac');
+ const u_Color = new UniformColor(program, 'u_Color');
+
+ // holds vertices for triangle
+ const coords = new Float32Array(6);
+ const coordsBuffer = newDynamicBuffer();
+
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+
+ let sizeFracX = 0.5;
+ const sizeX = sizeFracX * viewport.w;
+ const sizeY = sizeX * Math.sqrt(3) / 2;
+ let sizeFracY = sizeY / viewport.h;
+
+ const bufferFracX = 0.05;
+ const bufferSize = bufferFracX * viewport.w;
+ const bufferFracY = bufferSize / viewport.h;
+
+ const centerFracX = 0.5;
+ const centerFracY = bufferFracY + sizeFracY / 2;
+
+ if (group.collapsed) {
+
+ sizeFracX = sizeY / viewport.w;
+ sizeFracY = sizeX / viewport.h;
+
+ const fracStartX = centerFracX - sizeFracX / 2;
+ const fracEndX = centerFracX + sizeFracX / 2;
+
+ const fracStartY = 1 - (centerFracY - sizeFracY / 2);
+ const fracEndY = 1 - (centerFracY + sizeFracY / 2);
+
+ let index = 0;
+ coords[index++] = fracStartX;
+ coords[index++] = fracStartY;
+ coords[index++] = fracEndX;
+ coords[index++] = (fracStartY + fracEndY) / 2;
+ coords[index++] = fracStartX;
+ coords[index++] = fracEndY;
+ } else {
+ const fracStartX = centerFracX - sizeFracX / 2;
+ const fracEndX = centerFracX + sizeFracX / 2;
+
+ const fracStartY = 1 - (centerFracY - sizeFracY / 2);
+ const fracEndY = 1 - (centerFracY + sizeFracY / 2);
+
+ let index = 0;
+ coords[index++] = fracStartX;
+ coords[index++] = fracStartY;
+ coords[index++] = fracEndX;
+ coords[index++] = fracStartY;
+ coords[index++] = (fracStartX + fracEndX) / 2;
+ coords[index++] = fracEndY;
+ }
- var groupArrowPane = new Pane( { updatePrefSize: function( parentPrefSize : Size ) {
+ program.use(gl);
+ coordsBuffer.setData(coords);
+ a_XyFrac.setDataAndEnable(gl, coordsBuffer, 2, GL.FLOAT);
+
+ u_Color.setData(gl, white);
+ gl.drawArrays(GL.TRIANGLES, 0, 3);
+
+ a_XyFrac.disable(gl);
+ program.endUse(gl);
+ };
+}
+
+interface TimelineContentPaneOptions {
+ selectedIntervalMode: string;
+ rowPaneFactoryChooser: TimelineRowPaneFactoryChooser;
+
+ font: string;
+ fgColor: Color;
+ rowLabelColor: Color;
+ rowLabelBgColor: Color;
+ rowHighlightColor: Color;
+ rowHighlightWidth: number;
+ rowHighlightInsets: Insets;
+ groupLabelColor: Color;
+ groupHighlightColor: Color;
+ groupHighlightWidth: number;
+ groupHighlightInsets: Insets;
+ axisLabelColor: Color;
+ bgColor: Color;
+ rowBgColor: Color;
+ rowAltBgColor: Color;
+ gridColor: Color;
+
+ gridTimeZone: string;
+ gridTickSpacing: number;
+ referenceDate: string;
+
+ groupLabelInsets: Insets;
+ rowLabelInsets: Insets;
+ rowLabelPaneWidth: number;
+ rowSeparatorHeight: number;
+
+ draggableEdgeWidth: number;
+ snapToDistance: number;
+
+ mouseWheelListener: (PointerEvent: PointerEvent) => void;
+}
+
+interface TimelineContentPaneArguments {
+ drawable: Drawable;
+ scrollLayout: VerticalScrollLayout;
+ timeAxis: TimeAxis1D;
+ model: TimelineModel;
+ ui: TimelineUi;
+ options: TimelineContentPaneOptions;
+}
+
+function newTimelineContentPane(args: TimelineContentPaneArguments): Pane {
+ const drawable = args.drawable;
+ const scrollLayout = args.scrollLayout;
+ const timeAxis = args.timeAxis;
+ const model = args.model;
+ const ui = args.ui;
+ const options = args.options;
+
+ const root = model.root;
+
+ const selectedIntervalMode = options.selectedIntervalMode;
+ const rowPaneFactoryChooser = options.rowPaneFactoryChooser;
+
+ const font = options.font;
+ const fgColor = options.fgColor;
+ const rowLabelColor = options.rowLabelColor;
+ const groupLabelColor = options.groupLabelColor;
+ const groupHighlightColor = options.groupHighlightColor;
+ const groupHighlightWidth = options.groupHighlightWidth;
+ const groupHighlightInsets = options.groupHighlightInsets;
+ const axisLabelColor = options.axisLabelColor;
+ const bgColor = options.bgColor;
+ const rowBgColor = options.rowBgColor;
+ const rowAltBgColor = options.rowAltBgColor;
+ const gridColor = options.gridColor;
+
+ const gridTimeZone = options.gridTimeZone;
+ const gridTickSpacing = options.gridTickSpacing;
+
+ const groupLabelInsets = options.groupLabelInsets;
+ const rowLabelInsets = options.rowLabelInsets;
+ const rowLabelPaneWidth = options.rowLabelPaneWidth;
+ const rowSeparatorHeight = options.rowSeparatorHeight;
+
+ const draggableEdgeWidth = options.draggableEdgeWidth;
+ const snapToDistance = options.snapToDistance;
+
+ const textureRenderer = new TextureRenderer();
+ const createGroupLabelTexture = createTextTextureFactory(font);
+ const createRowLabelTexture = createTextTextureFactory(font);
+
+ // Group panes
+ //
+
+ const timelineContentPane = new Pane(newRowLayout());
+
+ const groupHeaderPanes: StringMap = {};
+ const groupContentPanes: StringMap = {};
+ const groupContainerPanes: StringMap = {};
+
+ const addGroup = function (groupGuid: string, groupIndex: number) {
+ const group = model.group(groupGuid);
+
+ const groupLabelFont = hasval(group.labelFont) ? group.labelFont : font;
+ const groupLabel = new Label(group.label, groupLabelFont, groupLabelColor);
+ const groupLabelPane = new Pane({ updatePrefSize: fitToLabel(groupLabel) }, false);
+ groupLabelPane.addPainter(newLabelPainter(groupLabel, 0, 1, 0, 1));
+
+ const groupArrowPane = new Pane({
+ updatePrefSize: function (parentPrefSize: Size) {
parentPrefSize.w = 16;
parentPrefSize.h = 0;
- } }, false );
- groupArrowPane.addPainter( newGroupCollapseExpandArrowPainter( group ) );
-
- var groupPane = new Pane( newColumnLayout( ), false );
- groupPane.addPane( groupArrowPane, 0 );
- groupPane.addPane( groupLabelPane, 1 );
-
- var groupButton = newInsetPane( groupPane, groupLabelInsets, bgColor );
-
-
- var redrawLabel = function( ) {
- groupLabel.text = group.label;
- drawable.redraw( );
}
- group.attrsChanged.on( redrawLabel );
-
- /// handle rollup group row ///
-
- var groupHeaderStripe = new Pane( newRowLayout( ) );
- groupHeaderStripe.addPane( new Pane( null ), 0, { height: null } );
- groupHeaderStripe.addPane( newSolidPane( groupLabelColor ), 1, { height: 1 } );
- groupHeaderStripe.addPane( new Pane( null ), 2, { height: null } );
-
- var rollupRow = model.row( group.rollupGuid );
- if ( rollupRow ) {
-
- var rowBackgroundPanes = newRowBackgroundPanes( args, group.rowGuids, rollupRow );
- var rowBackgroundPane = rowBackgroundPanes.rowBackgroundPane;
- var rowInsetPane = rowBackgroundPanes.rowInsetPane;
-
- var rollupUi = ui.rowUi( rollupRow.rowGuid );
-
- // expose panes in api via TimelineRowUi
- rollupUi.addPane( 'background', rowBackgroundPane );
- rollupUi.addPane( 'inset', rowInsetPane );
-
- var rollupDataAxis = rollupRow.dataAxis;
-
- var rollupContentPane : Pane = null;
- var rollupPaneFactory : TimelineRowPaneFactory = null;
- var rollupContentOptions = { timelineFont: font, timelineFgColor: fgColor, draggableEdgeWidth: draggableEdgeWidth, snapToDistance: snapToDistance, isMaximized: false, mouseWheelListener: args.options.mouseWheelListener };
- var refreshRollupContentPane = function( ) {
- var newRollupPaneFactory = ( rollupUi.paneFactory || rowPaneFactoryChooser( rollupRow ) );
- if ( newRollupPaneFactory !== rollupPaneFactory ) {
- if ( rollupContentPane ) {
- rollupContentPane.dispose.fire( );
- rowInsetPane.removePane( rollupContentPane );
- }
- rollupPaneFactory = newRollupPaneFactory;
- rollupContentPane = ( rollupPaneFactory && rollupPaneFactory( drawable, timeAxis, rollupDataAxis, model, rollupRow, ui, rollupContentOptions ) );
- if ( rollupContentPane ) {
- rowInsetPane.addPane( rollupContentPane );
- }
- drawable.redraw( );
+ }, false);
+ groupArrowPane.addPainter(newGroupCollapseExpandArrowPainter(group));
+
+ const groupPane = new Pane(newColumnLayout(), false);
+ groupPane.addPane(groupArrowPane, 0);
+ groupPane.addPane(groupLabelPane, 1);
+
+ const groupButton = newInsetPane(groupPane, groupLabelInsets, bgColor);
+
+
+ const redrawLabel = function () {
+ groupLabel.text = group.label;
+ drawable.redraw();
+ };
+ group.attrsChanged.on(redrawLabel);
+ /// handle rollup group row ///
+
+ const groupHeaderStripe = new Pane(newRowLayout());
+ groupHeaderStripe.addPane(new Pane(null), 0, { height: null });
+ groupHeaderStripe.addPane(newSolidPane(groupLabelColor), 1, { height: 1 });
+ groupHeaderStripe.addPane(new Pane(null), 2, { height: null });
+
+ const rollupRow = model.row(group.rollupGuid);
+ let groupHeaderPane: Pane = null;
+ let groupHeaderUnderlay: Pane = null;
+ if (rollupRow) {
+
+ const rowBackgroundPanes = newRowBackgroundPanes(args, group.rowGuids, rollupRow);
+ const rowBackgroundPane = rowBackgroundPanes.rowBackgroundPane;
+ const rowInsetPane = rowBackgroundPanes.rowInsetPane;
+
+ const rollupUi = ui.rowUi(rollupRow.rowGuid);
+
+ // expose panes in api via TimelineRowUi
+ rollupUi.addPane('background', rowBackgroundPane);
+ rollupUi.addPane('inset', rowInsetPane);
+
+ const rollupDataAxis = rollupRow.dataAxis;
+
+ let rollupContentPane: Pane = null;
+ let rollupPaneFactory: TimelineRowPaneFactory = null;
+ const rollupContentOptions = { timelineFont: font, timelineFgColor: fgColor, draggableEdgeWidth: draggableEdgeWidth, snapToDistance: snapToDistance, isMaximized: false, mouseWheelListener: args.options.mouseWheelListener };
+ const refreshRollupContentPane = function () {
+ const newRollupPaneFactory = (rollupUi.paneFactory || rowPaneFactoryChooser(rollupRow));
+ if (newRollupPaneFactory !== rollupPaneFactory) {
+ if (rollupContentPane) {
+ rollupContentPane.dispose.fire();
+ rowInsetPane.removePane(rollupContentPane);
}
- };
-
- rollupUi.paneFactoryChanged.on( refreshRollupContentPane );
- rollupRow.attrsChanged.on( refreshRollupContentPane );
- rollupRow.eventGuids.valueAdded.on( refreshRollupContentPane );
- rollupRow.eventGuids.valueRemoved.on( refreshRollupContentPane );
- rollupRow.timeseriesGuids.valueAdded.on( refreshRollupContentPane );
- rollupRow.timeseriesGuids.valueRemoved.on( refreshRollupContentPane );
- refreshRollupContentPane( );
-
- var groupButtonHeaderUnderlay = new Pane( newColumnLayout( ) );
- groupButtonHeaderUnderlay.addPane( groupButton, 0 );
- groupButtonHeaderUnderlay.addPane( groupHeaderStripe, 1, { ignoreHeight: true } );
-
- var groupHeaderUnderlay = new Pane( newColumnLayout( ) );
- groupHeaderUnderlay.addPainter( newBackgroundPainter( bgColor ) );
- groupHeaderUnderlay.addPane( groupButtonHeaderUnderlay, 0, { width: rowLabelPaneWidth } );
- groupHeaderUnderlay.addPane( rowBackgroundPane, 1, { width: null } );
-
- var groupHeaderPane = groupHeaderUnderlay;
- }
- else {
-
- var groupHeaderUnderlay = new Pane( newColumnLayout( ) );
- groupHeaderUnderlay.addPainter( newBackgroundPainter( bgColor ) );
- groupHeaderUnderlay.addPane( groupButton, 0 );
- groupHeaderUnderlay.addPane( groupHeaderStripe, 1, { ignoreHeight: true } );
-
- var groupHeaderOverlay = newTimeAxisPane( args, null );
- var groupHeaderOverlayInsets = newInsets( 0, 0, 0, rowLabelPaneWidth );
-
- var groupHeaderPane = new Pane( newOverlayLayout( ) );
- groupHeaderPane.addPane( groupHeaderUnderlay, true );
- groupHeaderPane.addPane( newInsetPane( groupHeaderOverlay, groupHeaderOverlayInsets, null, false ), false );
-
- }
-
- var groupContentPane = new Pane( newRowLayout( ) );
-
- timelineContentPane.updateLayoutArgs( function( layoutArg : any ) : any {
- var shift = ( isNumber( layoutArg ) && layoutArg >= 2*groupIndex );
- return ( shift ? layoutArg + 2 : layoutArg );
- } );
- timelineContentPane.addPane( groupHeaderPane, 2*groupIndex );
- timelineContentPane.addPane( groupContentPane, 2*groupIndex + 1, { hide: group.collapsed } );
- groupHeaderPanes[ groupGuid ] = groupHeaderPane;
- groupContentPanes[ groupGuid ] = groupContentPane;
-
- var groupAttrsChanged = function( ) {
- var groupContentLayoutOpts = timelineContentPane.layoutOptions( groupContentPane );
- if ( group.collapsed !== groupContentLayoutOpts.hide ) {
- groupContentLayoutOpts.hide = group.collapsed;
- drawable.redraw( );
+ rollupPaneFactory = newRollupPaneFactory;
+ rollupContentPane = (rollupPaneFactory && rollupPaneFactory(drawable, timeAxis, rollupDataAxis, model, rollupRow, ui, rollupContentOptions));
+ if (rollupContentPane) {
+ rowInsetPane.addPane(rollupContentPane);
+ }
+ drawable.redraw();
}
};
- group.attrsChanged.on( groupAttrsChanged );
- groupButton.mouseDown.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) ) {
- group.collapsed = !group.collapsed;
- }
- } );
-
- // Handle hidden property
- //
-
- timelineContentPane.layoutOptions( groupContentPane ).hide = group.hidden;
- timelineContentPane.layoutOptions( groupHeaderPane ).hide = group.hidden;
-
- setupRowContainerPane( args, groupContentPane, group.rowGuids, false, group.groupGuid );
-
- groupContentPane.dispose.on( function( ) {
- group.attrsChanged.off( redrawLabel );
- group.attrsChanged.off( groupAttrsChanged );
- });
-
- };
- root.groupGuids.forEach( addGroup );
- root.groupGuids.valueAdded.on( addGroup );
-
- var moveGroup = function( groupGuid : string, groupOldIndex : number, groupNewIndex : number ) {
- var nMin = Math.min( groupOldIndex, groupNewIndex );
- var nMax = Math.max( groupOldIndex, groupNewIndex );
- for ( var n = nMin; n <= nMax; n++ ) {
- var groupGuid = root.groupGuids.valueAt( n );
- timelineContentPane.setLayoutArg( groupHeaderPanes[ groupGuid ], 2*n );
- timelineContentPane.setLayoutArg( groupContentPanes[ groupGuid ], 2*n + 1 );
+ rollupUi.paneFactoryChanged.on(refreshRollupContentPane);
+ rollupRow.attrsChanged.on(refreshRollupContentPane);
+ rollupRow.eventGuids.valueAdded.on(refreshRollupContentPane);
+ rollupRow.eventGuids.valueRemoved.on(refreshRollupContentPane);
+ rollupRow.timeseriesGuids.valueAdded.on(refreshRollupContentPane);
+ rollupRow.timeseriesGuids.valueRemoved.on(refreshRollupContentPane);
+ refreshRollupContentPane();
+
+ const groupButtonHeaderUnderlay = new Pane(newColumnLayout());
+ groupButtonHeaderUnderlay.addPane(groupButton, 0);
+ groupButtonHeaderUnderlay.addPane(groupHeaderStripe, 1, { ignoreHeight: true });
+
+ groupHeaderUnderlay = new Pane(newColumnLayout());
+ groupHeaderUnderlay.addPainter(newBackgroundPainter(bgColor));
+ groupHeaderUnderlay.addPane(groupButtonHeaderUnderlay, 0, { width: rowLabelPaneWidth });
+ groupHeaderUnderlay.addPane(rowBackgroundPane, 1, { width: null });
+
+ groupHeaderPane = groupHeaderUnderlay;
+ }
+ else {
+
+ groupHeaderUnderlay = new Pane(newColumnLayout());
+ groupHeaderUnderlay.addPainter(newBackgroundPainter(bgColor));
+ groupHeaderUnderlay.addPane(groupButton, 0);
+ groupHeaderUnderlay.addPane(groupHeaderStripe, 1, { ignoreHeight: true });
+ const groupHeaderOverlay = newTimeAxisPane(args, null, group.cursor);
+ const groupHeaderOverlayInsets = newInsets(0, 0, 0, rowLabelPaneWidth);
+
+ groupHeaderPane = new Pane(newOverlayLayout());
+ groupHeaderPane.addPane(groupHeaderUnderlay, true);
+ groupHeaderPane.addPane(newInsetPane(groupHeaderOverlay, groupHeaderOverlayInsets, null, false), false);
+
+ }
+
+ const groupHeaderHighlight = new Pane(newColumnLayout(), false);
+ const groupHighlight = new Highlight(group.highlightColor || groupHighlightColor);
+ const highlightInnerPane = new Pane(null);
+ highlightInnerPane.addPainter(groupHighlight.newPainter());
+ const insets = group.highlightInsets || groupHighlightInsets;
+ const width = group.highlightWidth || groupHighlightWidth;
+ const containerWidth = insets ? insets.left + insets.right + width : width;
+ const highlightPane = newInsetPane(highlightInnerPane, insets);
+ groupHeaderHighlight.addPane(highlightPane, 0, { width: containerWidth, height: null });
+
+ const groupContainerOverlayPane = new Pane(newOverlayLayout());
+ const groupContainerRowPane = new Pane(newRowLayout());
+ const groupContentPane = new Pane(newRowLayout());
+ groupContainerOverlayPane.addPane(groupContainerRowPane, true, { width: null, height: null });
+ groupContainerOverlayPane.addPane(groupHeaderHighlight, false, { width: null, height: null });
+
+ groupContainerRowPane.updateLayoutArgs(function (layoutArg: any): any {
+ const shift = (isNumber(layoutArg) && layoutArg >= 2 * groupIndex);
+ return (shift ? layoutArg + 2 : layoutArg);
+ });
+
+ groupContainerRowPane.addPane(groupHeaderPane, 2 * groupIndex);
+ groupContainerRowPane.addPane(groupContentPane, 2 * groupIndex + 1, { hide: group.collapsed });
+
+ timelineContentPane.addPane(groupContainerOverlayPane, groupIndex);
+
+ groupHeaderPanes[groupGuid] = groupHeaderPane;
+ groupContentPanes[groupGuid] = groupContentPane;
+ groupContainerPanes[groupGuid] = groupContainerOverlayPane;
+
+ const groupAttrsChanged = function (timelineGroup: TimelineGroup) {
+ const groupContentLayoutOpts = groupContainerRowPane.layoutOptions(groupContentPane);
+ const groupHighlightLayoutOpts = groupHeaderHighlight.layoutOptions(highlightPane);
+ let redraw = false;
+ if (timelineGroup.highlighted !== (!groupHighlightLayoutOpts.hide)) {
+ groupHighlightLayoutOpts.hide = !timelineGroup.highlighted;
+ redraw = true;
+ }
+ if (timelineGroup.highlightColor !== groupHighlight.color) {
+ groupHighlight.color = timelineGroup.highlightColor;
+ redraw = true;
}
+ if (timelineGroup.dashPattern !== groupHighlight.dashPattern) {
+ groupHighlight.dashPattern = timelineGroup.dashPattern;
+ redraw = true;
+ }
+ if (timelineGroup.dashLength !== groupHighlight.dashLength) {
+ groupHighlight.dashLength = timelineGroup.dashLength;
+ redraw = true;
+ }
+ if (timelineGroup.collapsed !== groupContentLayoutOpts.hide) {
+ groupContentLayoutOpts.hide = timelineGroup.collapsed;
+ redraw = true;
+ }
+ if (timelineGroup.labelFont !== groupContentLayoutOpts.labelFont) {
+ groupLabel.font = timelineGroup.labelFont;
+ redraw = true;
+ }
+ if (redraw) {
+ drawable.redraw();
+ }
+ }.bind(this, group);
+ group.attrsChanged.on(groupAttrsChanged);
+ groupAttrsChanged();
- drawable.redraw( );
- };
- root.groupGuids.valueMoved.on( moveGroup );
-
- var removeGroup = function( groupGuid : string, groupIndex : number ) {
- var contentPane : Pane = groupContentPanes[ groupGuid ];
- var headerPane : Pane = groupHeaderPanes[ groupGuid ];
- contentPane.dispose.fire( );
- headerPane.dispose.fire( );
- timelineContentPane.removePane( contentPane );
- timelineContentPane.removePane( headerPane );
- timelineContentPane.updateLayoutArgs( function( layoutArg : any ) : any {
- var shift = ( isNumber( layoutArg ) && layoutArg > 2*groupIndex + 1 );
- return ( shift ? layoutArg - 2 : layoutArg );
- } );
- delete groupHeaderPanes[ groupGuid ];
- delete groupContentPanes[ groupGuid ];
-
- drawable.redraw( );
- };
- root.groupGuids.valueRemoved.on( removeGroup );
-
- // Handle listing for hidden property
+ groupButton.mouseDown.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent)) {
+ group.collapsed = !group.collapsed;
+ }
+ });
+
+ // Handle hidden property
//
-
- var groupAttrsChangedListeners = {};
-
- var attachGroupAttrsChangedListener = function( groupGuid : string, groupIndex : number ) {
- var group = model.group( groupGuid );
- var groupAttrsChangedListener = function( ) {
- if ( hasval( group.hidden ) && hasval( groupContentPanes[groupGuid] ) ) {
-
- timelineContentPane.layoutOptions( groupContentPanes[groupGuid] ).hide = group.hidden;
- timelineContentPane.layoutOptions( groupHeaderPanes[groupGuid] ).hide = group.hidden;
- drawable.redraw( );
- }
- };
- groupAttrsChangedListeners[ groupGuid ] = groupAttrsChangedListener;
- group.attrsChanged.on( groupAttrsChangedListener );
- };
-
- var unattachGroupAttrsChangedListener = function( groupGuid : string, groupIndex : number ) {
- var group = model.group( groupGuid );
- group.attrsChanged.off( groupAttrsChangedListeners[ groupGuid ] );
+
+ groupContainerRowPane.layoutOptions(groupContentPane).hide = group.hidden;
+ groupContainerRowPane.layoutOptions(groupHeaderPane).hide = group.hidden;
+
+ setupRowContainerPane(args, groupContentPane, group.rowGuids, false, group.groupGuid);
+
+ groupContentPane.dispose.on(function () {
+ group.attrsChanged.off(redrawLabel);
+ group.attrsChanged.off(groupAttrsChanged);
+ });
+
+ };
+ root.groupGuids.forEach(addGroup);
+ root.groupGuids.valueAdded.on(addGroup);
+
+ const moveGroup = function (groupGuid: string, groupOldIndex: number, groupNewIndex: number) {
+ const nMin = Math.min(groupOldIndex, groupNewIndex);
+ const nMax = Math.max(groupOldIndex, groupNewIndex);
+ for (let n = nMin; n <= nMax; n++) {
+ const groupGuidTemp = root.groupGuids.valueAt(n);
+ timelineContentPane.setLayoutArg(groupContainerPanes[groupGuidTemp], n);
+ // timelineContentPane.setLayoutArg(groupHeaderPanes[groupGuidTemp], 2 * n);
+ // timelineContentPane.setLayoutArg(groupContentPanes[groupGuidTemp], 2 * n + 1);
}
-
- root.groupGuids.forEach( attachGroupAttrsChangedListener );
- root.groupGuids.valueAdded.on( attachGroupAttrsChangedListener );
- root.groupGuids.valueRemoved.on( unattachGroupAttrsChangedListener );
- // Dispose
- //
-
- timelineContentPane.dispose.on( function( ) {
- root.groupGuids.valueAdded.off( addGroup );
- root.groupGuids.valueMoved.off( moveGroup );
- root.groupGuids.valueRemoved.off( removeGroup );
- } );
-
- return timelineContentPane;
- }
-
- // Row panes and painters
+ drawable.redraw();
+ };
+ root.groupGuids.valueMoved.on(moveGroup);
+
+ const removeGroup = function (groupGuid: string, groupIndex: number) {
+ // const contentPane: Pane = groupContentPanes[groupGuid];
+ // const headerPane: Pane = groupHeaderPanes[groupGuid];
+ const groupPane: Pane = groupContainerPanes[groupGuid];
+ // contentPane.dispose.fire();
+ // headerPane.dispose.fire();
+ groupPane.dispose.fire();
+ // timelineContentPane.removePane(contentPane);
+ // timelineContentPane.removePane(headerPane);
+ timelineContentPane.removePane(groupPane);
+ timelineContentPane.updateLayoutArgs(function (layoutArg: any): any {
+ // const shift = (isNumber(layoutArg) && layoutArg > 2 * groupIndex + 1);
+ // return (shift ? layoutArg - 2 : layoutArg);
+ const shift = (isNumber(layoutArg) && layoutArg > groupIndex);
+ return (shift ? layoutArg - 1 : layoutArg);
+ });
+ // delete groupHeaderPanes[groupGuid];
+ // delete groupContentPanes[groupGuid];
+ delete groupContainerPanes[groupGuid];
+
+ drawable.redraw();
+ };
+ root.groupGuids.valueRemoved.on(removeGroup);
+
+ // Handle listing for hidden property
//
-
- function newRowBackgroundPainter( args : TimelineContentPaneArguments, guidList : OrderedStringSet, row : TimelineRowModel ) {
- return function( gl : WebGLRenderingContext ) {
- var color = hasval( row.bgColor ) ? row.bgColor : ( guidList.indexOf( row.rowGuid ) % 2 ? args.options.rowBgColor : args.options.rowAltBgColor );
- gl.clearColor( color.r, color.g, color.b, color.a );
- gl.clear( GL.COLOR_BUFFER_BIT );
- };
- }
-
- function newRowBackgroundPanes( args : TimelineContentPaneArguments, guidList : OrderedStringSet, row : TimelineRowModel ) {
- var rowBackgroundPane = newTimeAxisPane( args, row );
- rowBackgroundPane.addPainter( newRowBackgroundPainter( args, guidList, row ) );
-
- var timeGridOpts = { tickSpacing: args.options.gridTickSpacing, gridColor: args.options.gridColor, referenceDate: args.options.referenceDate };
- rowBackgroundPane.addPainter( newTimeGridPainter( args.timeAxis, false, args.options.gridTimeZone, timeGridOpts ) );
-
- var rowInsetTop = args.options.rowSeparatorHeight/2;
- var rowInsetBottom = args.options.rowSeparatorHeight - rowInsetTop;
- var rowInsetPane = new Pane( newInsetLayout( newInsets( rowInsetTop, 0, rowInsetBottom, 0 ) ), false );
- rowInsetPane.addPainter( newBorderPainter( args.options.bgColor, { thickness: rowInsetTop, drawRight: false, drawLeft: false, drawBottom: false } ) );
- rowInsetPane.addPainter( newBorderPainter( args.options.bgColor, { thickness: rowInsetBottom, drawRight: false, drawLeft: false, drawTop: false } ) );
- rowBackgroundPane.addPane( rowInsetPane, true );
-
- var rowOverlayPane = new Pane( null, false );
- rowOverlayPane.addPainter( newBorderPainter( args.options.rowLabelColor, { drawRight: false, drawTop: false, drawBottom: false } ) );
- rowBackgroundPane.addPane( rowOverlayPane, false );
-
- return { rowInsetPane : rowInsetPane, rowBackgroundPane : rowBackgroundPane };
- }
-
- function setupRowContainerPane( args : TimelineContentPaneArguments, parentPane : Pane, guidList : OrderedStringSet, isMaximized : boolean, keyPrefix : string ) {
-
- var drawable = args.drawable;
- var scrollLayout = args.scrollLayout;
- var timeAxis = args.timeAxis;
- var model = args.model;
- var ui = args.ui;
- var options = args.options;
-
- var rowPanes : StringMap = {};
-
- var addRow = function( rowGuid : string, rowIndex : number ) {
- var row = model.row( rowGuid );
- var rowUi = ui.rowUi( rowGuid );
-
- var rowLabelColorBg : Color = hasval( row.bgLabelColor ) ? row.bgLabelColor : options.rowLabelBgColor;
- var rowLabelColorFg : Color = hasval( row.fgLabelColor ) ? row.fgLabelColor : options.rowLabelColor;
- var rowLabelFont : string = hasval( row.labelFont ) ? row.labelFont : options.font;
-
- var rowLabel = new Label( row.label, rowLabelFont, rowLabelColorFg );
- var rowLabelPane = new Pane( { updatePrefSize: fitToLabel( rowLabel ) }, false );
- rowLabelPane.addPainter( newLabelPainter( rowLabel, 0, 0.5, 0, 0.5 ) );
-
- var rowLabelBackground = new Background( rowLabelColorBg );
- var rowHeaderPane = new Pane( newInsetLayout( options.rowLabelInsets ), true );
- rowHeaderPane.addPainter( rowLabelBackground.newPainter( ) );
- rowHeaderPane.addPane( rowLabelPane );
-
- var rowAttrsChanged = function( ) {
- rowLabel.text = row.label;
- rowLabel.fgColor = hasval( row.fgLabelColor ) ? row.fgLabelColor : options.rowLabelColor;
- rowLabel.font = hasval( row.labelFont ) ? row.labelFont : options.font;
- rowLabelBackground.color = hasval( row.bgLabelColor ) ? row.bgLabelColor : options.bgColor;
- drawable.redraw( );
- }
- row.attrsChanged.on( rowAttrsChanged );
-
- var rowBackgroundPanes = newRowBackgroundPanes( args, guidList, row );
- var rowBackgroundPane = rowBackgroundPanes.rowBackgroundPane;
- var rowInsetPane = rowBackgroundPanes.rowInsetPane;
-
- var rowPane = new Pane( newColumnLayout( ) );
- rowPane.addPane( rowHeaderPane, 0, { width: options.rowLabelPaneWidth } );
- rowPane.addPane( rowBackgroundPane, 1, { width: null } );
-
- // expose panes in api via TimelineRowUi
- rowUi.addPane( keyPrefix+'-background', rowBackgroundPane );
- rowUi.addPane( keyPrefix+'-inset', rowInsetPane );
- rowUi.addPane( keyPrefix+'-label', rowLabelPane );
- rowUi.addPane( keyPrefix+'-header', rowHeaderPane );
-
- var rowDataAxis = row.dataAxis;
-
- var rowContentPane : Pane = null;
- var rowPaneFactory : TimelineRowPaneFactory = null;
- var rowContentOptions = { timelineFont: options.font, timelineFgColor: options.fgColor, draggableEdgeWidth: options.draggableEdgeWidth, snapToDistance: options.snapToDistance, isMaximized: isMaximized, mouseWheelListener: options.mouseWheelListener };
- var refreshRowContentPane = function( ) {
- var newRowPaneFactory = ( rowUi.paneFactory || options.rowPaneFactoryChooser( row ) );
- if ( newRowPaneFactory !== rowPaneFactory ) {
- if ( rowContentPane ) {
- rowContentPane.dispose.fire( );
- rowInsetPane.removePane( rowContentPane );
- }
- rowPaneFactory = newRowPaneFactory;
- rowContentPane = ( rowPaneFactory && rowPaneFactory( drawable, timeAxis, rowDataAxis, model, row, ui, rowContentOptions ) );
- if ( rowContentPane ) {
- rowInsetPane.addPane( rowContentPane );
- }
- drawable.redraw( );
- }
- };
-
- rowUi.paneFactoryChanged.on( refreshRowContentPane );
- row.attrsChanged.on( refreshRowContentPane );
- row.eventGuids.valueAdded.on( refreshRowContentPane );
- row.eventGuids.valueRemoved.on( refreshRowContentPane );
- row.timeseriesGuids.valueAdded.on( refreshRowContentPane );
- row.timeseriesGuids.valueRemoved.on( refreshRowContentPane );
- refreshRowContentPane( );
-
- parentPane.updateLayoutArgs( function( layoutArg : any ) : any {
- var shift = ( isNumber( layoutArg ) && layoutArg >= rowIndex );
- return ( shift ? layoutArg + 1 : layoutArg );
- } );
- parentPane.addPane( rowPane, rowIndex );
- rowPanes[ rowGuid ] = rowPane;
-
-
- // Handle hidden property
- //
- parentPane.layoutOptions( rowPane ).hide = row.hidden;
-
- drawable.redraw( );
-
- rowPane.dispose.on( function( ) {
- row.attrsChanged.off( rowAttrsChanged );
- rowUi.paneFactoryChanged.off( refreshRowContentPane );
- row.attrsChanged.off( refreshRowContentPane );
- row.eventGuids.valueAdded.off( refreshRowContentPane );
- row.eventGuids.valueRemoved.off( refreshRowContentPane );
- row.timeseriesGuids.valueAdded.off( refreshRowContentPane );
- row.timeseriesGuids.valueRemoved.off( refreshRowContentPane );
-
- rowUi.removePane( keyPrefix+'-background' );
- rowUi.removePane( keyPrefix+'-inset' );
- rowUi.removePane( keyPrefix+'-label' );
- rowUi.removePane( keyPrefix+'-header' );
- } );
- };
- guidList.forEach( addRow );
- guidList.valueAdded.on( addRow );
-
- var valueMoved = function( rowGuid : string, rowOldIndex : number, rowNewIndex : number ) {
- var nMin = Math.min( rowOldIndex, rowNewIndex );
- var nMax = Math.max( rowOldIndex, rowNewIndex );
- for ( var n = nMin; n <= nMax; n++ ) {
- var rowGuid = guidList.valueAt( n );
- parentPane.setLayoutArg( rowPanes[ rowGuid ], n );
+
+ const groupAttrsChangedListeners = {};
+
+ const attachGroupAttrsChangedListener = function (groupGuid: string, groupIndex: number) {
+ const group = model.group(groupGuid);
+ const groupAttrsChangedListener = function () {
+ if (hasval(group.hidden) && hasval(groupContentPanes[groupGuid])) {
+
+ timelineContentPane.layoutOptions(groupContentPanes[groupGuid]).hide = group.hidden;
+ timelineContentPane.layoutOptions(groupHeaderPanes[groupGuid]).hide = group.hidden;
+ drawable.redraw();
}
-
- drawable.redraw( );
};
- guidList.valueMoved.on( valueMoved );
-
- var removeRow = function( rowGuid : string, rowIndex : number ) {
- var pane : Pane = rowPanes[ rowGuid ];
- pane.dispose.fire( );
- parentPane.removePane( pane );
- parentPane.updateLayoutArgs( function( layoutArg : any ) : any {
- var shift = ( isNumber( layoutArg ) && layoutArg > rowIndex );
- return ( shift ? layoutArg - 1 : layoutArg );
- } );
- delete rowPanes[ rowGuid ];
-
- drawable.redraw( );
+ groupAttrsChangedListeners[groupGuid] = groupAttrsChangedListener;
+ group.attrsChanged.on(groupAttrsChangedListener);
+ };
+
+ const unattachGroupAttrsChangedListener = function (groupGuid: string, groupIndex: number) {
+ const group = model.group(groupGuid);
+ group.attrsChanged.off(groupAttrsChangedListeners[groupGuid]);
+ };
+
+ root.groupGuids.forEach(attachGroupAttrsChangedListener);
+ root.groupGuids.valueAdded.on(attachGroupAttrsChangedListener);
+ root.groupGuids.valueRemoved.on(unattachGroupAttrsChangedListener);
+
+ // Dispose
+ //
+
+ timelineContentPane.dispose.on(function () {
+ root.groupGuids.valueAdded.off(addGroup);
+ root.groupGuids.valueMoved.off(moveGroup);
+ root.groupGuids.valueRemoved.off(removeGroup);
+ });
+
+ return timelineContentPane;
+}
+
+// Row panes and painters
+//
+
+function newRowBackgroundPainter(args: TimelineContentPaneArguments, guidList: OrderedStringSet, row: TimelineRowModel) {
+ return function (gl: WebGLRenderingContext) {
+ const color = hasval(row.bgColor) ? row.bgColor : (guidList.indexOf(row.rowGuid) % 2 ? args.options.rowBgColor : args.options.rowAltBgColor);
+ gl.clearColor(color.r, color.g, color.b, color.a);
+ gl.clear(GL.COLOR_BUFFER_BIT);
+ };
+}
+
+function newRowBackgroundPanes(args: TimelineContentPaneArguments, guidList: OrderedStringSet, row: TimelineRowModel) {
+ const rowBackgroundPane = newTimeAxisPane(args, row, row.cursor);
+ rowBackgroundPane.addPainter(newRowBackgroundPainter(args, guidList, row));
+
+ const timeGridOpts = { tickSpacing: args.options.gridTickSpacing, gridColor: args.options.gridColor, referenceDate: args.options.referenceDate };
+ rowBackgroundPane.addPainter(newTimeGridPainter(args.timeAxis, false, args.options.gridTimeZone, timeGridOpts));
+
+ const rowInsetTop = args.options.rowSeparatorHeight / 2;
+ const rowInsetBottom = args.options.rowSeparatorHeight - rowInsetTop;
+ const rowInsetPane = new Pane(newInsetLayout(newInsets(rowInsetTop, 0, rowInsetBottom, 0)), false);
+ rowInsetPane.addPainter(newBorderPainter(args.options.bgColor, { thickness: rowInsetTop, drawRight: false, drawLeft: false, drawBottom: false }));
+ rowInsetPane.addPainter(newBorderPainter(args.options.bgColor, { thickness: rowInsetBottom, drawRight: false, drawLeft: false, drawTop: false }));
+ rowBackgroundPane.addPane(rowInsetPane, true);
+
+ const rowOverlayPane = new Pane(null, false);
+ rowOverlayPane.addPainter(newBorderPainter(args.options.rowLabelColor, { drawRight: false, drawTop: false, drawBottom: false }));
+ rowBackgroundPane.addPane(rowOverlayPane, false);
+
+ return { rowInsetPane: rowInsetPane, rowBackgroundPane: rowBackgroundPane };
+}
+
+function setupRowContainerPane(args: TimelineContentPaneArguments, parentPane: Pane, guidList: OrderedStringSet, isMaximized: boolean, keyPrefix: string) {
+
+ const drawable = args.drawable;
+ const scrollLayout = args.scrollLayout;
+ const timeAxis = args.timeAxis;
+ const model = args.model;
+ const ui = args.ui;
+ const options = args.options;
+
+ const rowContainerPanes: StringMap = {};
+
+ const addRow = function (rowGuid: string, rowIndex: number) {
+ const row = model.row(rowGuid);
+ const rowUi = ui.rowUi(rowGuid);
+
+ const rowLabelColorBg: Color = hasval(row.bgLabelColor) ? row.bgLabelColor : options.rowLabelBgColor;
+ const rowLabelColorFg: Color = hasval(row.fgLabelColor) ? row.fgLabelColor : options.rowLabelColor;
+ const rowLabelFont: string = hasval(row.labelFont) ? row.labelFont : options.font;
+
+ const rowLabel = new Label(row.label, rowLabelFont, rowLabelColorFg);
+ const rowLabelPane = new Pane({ updatePrefSize: fitToLabel(rowLabel) }, false);
+ rowLabelPane.addPainter(newLabelPainter(rowLabel, 0, 0.5, 0, 0.5, undefined, row.truncate));
+
+ const rowHeaderHighlight = new Pane(newColumnLayout(), false);
+ const rowHighlight = new Highlight(row.highlightColor || options.rowHighlightColor);
+ const highlightInnerPane = new Pane(newRowLayout());
+ highlightInnerPane.addPainter(rowHighlight.newPainter());
+ const insets = row.highlightInsets || options.rowHighlightInsets;
+ const width = row.highlightWidth || options.rowHighlightWidth;
+ const containerWidth = insets ? insets.left + insets.right + width : width;
+ const highlightPane = newInsetPane(highlightInnerPane, insets);
+ rowHeaderHighlight.addPane(highlightPane, 0, { width: containerWidth, height: null, hide: !row.highlighted });
+
+ const rowLabelBackground = new Background(rowLabelColorBg);
+ const rowHeaderPane = new Pane(newInsetLayout(options.rowLabelInsets), true);
+
+ rowHeaderPane.addPainter(rowLabelBackground.newPainter());
+ rowHeaderPane.addPane(rowLabelPane);
+
+ const rowBackgroundPanes = newRowBackgroundPanes(args, guidList, row);
+ const rowBackgroundPane = rowBackgroundPanes.rowBackgroundPane;
+ const rowInsetPane = rowBackgroundPanes.rowInsetPane;
+
+ const rowPane = new Pane(newColumnLayout());
+ rowPane.addPane(rowHeaderPane, 0, { width: options.rowLabelPaneWidth });
+ rowPane.addPane(rowBackgroundPane, 1, { width: null });
+
+ const rowContainerOverlayPane = new Pane(newOverlayLayout());
+ rowContainerOverlayPane.addPane(rowPane, true, { width: null, height: null });
+ rowContainerOverlayPane.addPane(rowHeaderHighlight, false, { width: null, height: null });
+
+ const rowAttrsChanged = function () {
+ rowLabel.text = row.label;
+ rowLabel.fgColor = hasval(row.fgLabelColor) ? row.fgLabelColor : options.rowLabelColor;
+ rowLabel.font = hasval(row.labelFont) ? row.labelFont : options.font;
+ rowLabelBackground.color = hasval(row.bgLabelColor) ? row.bgLabelColor : options.bgColor;
+
+ const rowHighlightLayoutOpts = rowHeaderHighlight.layoutOptions(highlightPane);
+ rowHighlightLayoutOpts.hide = !row.highlighted;
+ rowHighlight.color = row.highlightColor;
+
+ drawable.redraw();
};
- guidList.valueRemoved.on( removeRow );
-
- // Handle listing for hidden property
- //
-
- var attrsChangedListeners = {};
-
- var attachAttrsChangedListener = function( rowGuid : string, rowIndex : number ) {
- var row = model.row( rowGuid );
- var attrsChangedListener = function( ) {
- if ( hasval( row.hidden && hasval( rowPanes[rowGuid] ) ) ) {
- parentPane.layoutOptions( rowPanes[rowGuid] ).hide = row.hidden;
- drawable.redraw( );
+ row.attrsChanged.on(rowAttrsChanged);
+
+ // expose panes in api via TimelineRowUi
+ rowUi.addPane(keyPrefix + '-background', rowBackgroundPane);
+ rowUi.addPane(keyPrefix + '-inset', rowInsetPane);
+ rowUi.addPane(keyPrefix + '-label', rowLabelPane);
+ rowUi.addPane(keyPrefix + '-header', rowHeaderPane);
+
+ const rowDataAxis = row.dataAxis;
+
+ let rowContentPane: Pane = null;
+ let rowPaneFactory: TimelineRowPaneFactory = null;
+ const rowContentOptions = { timelineFont: options.font, timelineFgColor: options.fgColor, draggableEdgeWidth: options.draggableEdgeWidth, snapToDistance: options.snapToDistance, isMaximized: isMaximized, mouseWheelListener: options.mouseWheelListener };
+ const refreshRowContentPane = function () {
+ const newRowPaneFactory = (rowUi.paneFactory || options.rowPaneFactoryChooser(row));
+ if (newRowPaneFactory !== rowPaneFactory) {
+ if (rowContentPane) {
+ rowContentPane.dispose.fire();
+ rowInsetPane.removePane(rowContentPane);
}
- };
- attrsChangedListeners[ rowGuid ] = attrsChangedListener;
- row.attrsChanged.on( attrsChangedListener );
+ rowPaneFactory = newRowPaneFactory;
+ rowContentPane = (rowPaneFactory && rowPaneFactory(drawable, timeAxis, rowDataAxis, model, row, ui, rowContentOptions));
+ if (rowContentPane) {
+ rowInsetPane.addPane(rowContentPane);
+ }
+ drawable.redraw();
+ }
};
-
- var unattachAttrsChangedListener = function( rowGuid : string, rowIndex : number ) {
- var row = model.row( rowGuid );
- row.attrsChanged.off( attrsChangedListeners[ rowGuid ] );
- }
-
- guidList.forEach( attachAttrsChangedListener );
- guidList.valueAdded.on( attachAttrsChangedListener );
- guidList.valueRemoved.on( unattachAttrsChangedListener );
-
- // Redraw
+
+ rowUi.paneFactoryChanged.on(refreshRowContentPane);
+ row.attrsChanged.on(refreshRowContentPane);
+ row.eventGuids.valueAdded.on(refreshRowContentPane);
+ row.eventGuids.valueRemoved.on(refreshRowContentPane);
+ row.timeseriesGuids.valueAdded.on(refreshRowContentPane);
+ row.timeseriesGuids.valueRemoved.on(refreshRowContentPane);
+ refreshRowContentPane();
+
+ parentPane.updateLayoutArgs(function (layoutArg: any): any {
+ const shift = (isNumber(layoutArg) && layoutArg >= rowIndex);
+ return (shift ? layoutArg + 1 : layoutArg);
+ });
+ parentPane.addPane(rowContainerOverlayPane, rowIndex);
+ rowContainerPanes[rowGuid] = rowContainerOverlayPane;
+
+
+ // Handle hidden property
//
+ parentPane.layoutOptions(rowContainerOverlayPane).hide = row.hidden;
+
+ drawable.redraw();
+
+ rowContainerOverlayPane.dispose.on(function () {
+ row.attrsChanged.off(rowAttrsChanged);
+ rowUi.paneFactoryChanged.off(refreshRowContentPane);
+ row.attrsChanged.off(refreshRowContentPane);
+ row.eventGuids.valueAdded.off(refreshRowContentPane);
+ row.eventGuids.valueRemoved.off(refreshRowContentPane);
+ row.timeseriesGuids.valueAdded.off(refreshRowContentPane);
+ row.timeseriesGuids.valueRemoved.off(refreshRowContentPane);
+
+ rowUi.removePane(keyPrefix + '-background');
+ rowUi.removePane(keyPrefix + '-inset');
+ rowUi.removePane(keyPrefix + '-label');
+ rowUi.removePane(keyPrefix + '-header');
+ });
+ };
+ guidList.forEach(addRow);
+ guidList.valueAdded.on(addRow);
+
+ const valueMoved = function (rowGuid: string, rowOldIndex: number, rowNewIndex: number) {
+ const nMin = Math.min(rowOldIndex, rowNewIndex);
+ const nMax = Math.max(rowOldIndex, rowNewIndex);
+ for (let n = nMin; n <= nMax; n++) {
+ const rowGuidTemp = guidList.valueAt(n);
+ parentPane.setLayoutArg(rowContainerPanes[rowGuidTemp], n);
+ }
- drawable.redraw( );
-
- // Dispose
-
- parentPane.dispose.on( function( ) {
- guidList.valueAdded.off( addRow );
- guidList.valueMoved.off( valueMoved );
- guidList.valueRemoved.off( removeRow );
-
- guidList.valueAdded.off( attachAttrsChangedListener );
- guidList.valueRemoved.off( unattachAttrsChangedListener );
- } );
- }
+ drawable.redraw();
+ };
+ guidList.valueMoved.on(valueMoved);
+
+ const removeRow = function (rowGuid: string, rowIndex: number) {
+ const pane: Pane = rowContainerPanes[rowGuid];
+ pane.dispose.fire();
+ parentPane.removePane(pane);
+ parentPane.updateLayoutArgs(function (layoutArg: any): any {
+ const shift = (isNumber(layoutArg) && layoutArg > rowIndex);
+ return (shift ? layoutArg - 1 : layoutArg);
+ });
+ delete rowContainerPanes[rowGuid];
+
+ drawable.redraw();
+ };
+ guidList.valueRemoved.on(removeRow);
+
+ // Handle listing for hidden property
+ //
+
+ const attrsChangedListeners = {};
+
+ const attachAttrsChangedListener = function (rowGuid: string, rowIndex: number) {
+ const row = model.row(rowGuid);
+ const attrsChangedListener = function () {
+ if (hasval(row.hidden && hasval(rowContainerPanes[rowGuid]))) {
+ parentPane.layoutOptions(rowContainerPanes[rowGuid]).hide = row.hidden;
+ drawable.redraw();
+ }
+ };
+ attrsChangedListeners[rowGuid] = attrsChangedListener;
+ row.attrsChanged.on(attrsChangedListener);
+ };
+
+ const unattachAttrsChangedListener = function (rowGuid: string, rowIndex: number) {
+ const row = model.row(rowGuid);
+ row.attrsChanged.off(attrsChangedListeners[rowGuid]);
+ };
+
+ guidList.forEach(attachAttrsChangedListener);
+ guidList.valueAdded.on(attachAttrsChangedListener);
+ guidList.valueRemoved.on(unattachAttrsChangedListener);
+
+ // Redraw
+ //
+
+ drawable.redraw();
+
+ // Dispose
+ parentPane.dispose.on(function () {
+ guidList.valueAdded.off(addRow);
+ guidList.valueMoved.off(valueMoved);
+ guidList.valueRemoved.off(removeRow);
+ guidList.valueAdded.off(attachAttrsChangedListener);
+ guidList.valueRemoved.off(unattachAttrsChangedListener);
+ });
}
diff --git a/src/webglimpse/timeline/timeline_row.ts b/src/webglimpse/timeline/timeline_row.ts
index dc035c9..9cba153 100644
--- a/src/webglimpse/timeline/timeline_row.ts
+++ b/src/webglimpse/timeline/timeline_row.ts
@@ -27,28 +27,28 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { Color } from '../color';
+import { PointerEvent, Drawable, Pane } from '../core';
+import { TimeAxis1D } from './time_axis';
+import { Axis1D } from '../plot/axis';
+import { TimelineModel, TimelineRowModel } from './timeline_model';
+import { TimelineUi } from './timeline_ui';
- export interface TimelineRowPaneOptions {
- timelineFont : string;
- timelineFgColor : Color;
- draggableEdgeWidth : number;
- snapToDistance : number;
- isMaximized : boolean;
- mouseWheelListener? : ( PointerEvent ) => void;
- }
+export interface TimelineRowPaneOptions {
+ timelineFont: string;
+ timelineFgColor: Color;
+ draggableEdgeWidth: number;
+ snapToDistance: number;
+ isMaximized: boolean;
+ mouseWheelListener?: (PointerEvent: PointerEvent) => void;
+}
- export interface TimelineRowPaneFactory {
- ( drawable : Drawable,
- timeAxis : TimeAxis1D,
- dataAxis : Axis1D,
- model : TimelineModel,
- row : TimelineRowModel,
- ui : TimelineUi,
- options : TimelineRowPaneOptions ) : Pane;
- }
+export type TimelineRowPaneFactory = (drawable: Drawable,
+ timeAxis: TimeAxis1D,
+ dataAxis: Axis1D,
+ model: TimelineModel,
+ row: TimelineRowModel,
+ ui: TimelineUi,
+ options: TimelineRowPaneOptions) => Pane;
- export interface TimelineRowPaneFactoryChooser {
- ( row : TimelineRowModel ) : TimelineRowPaneFactory;
- }
-}
\ No newline at end of file
+export type TimelineRowPaneFactoryChooser = (row: TimelineRowModel) => TimelineRowPaneFactory;
diff --git a/src/webglimpse/timeline/timeline_styles.ts b/src/webglimpse/timeline/timeline_styles.ts
index 512bb4e..aa1167d 100644
--- a/src/webglimpse/timeline/timeline_styles.ts
+++ b/src/webglimpse/timeline/timeline_styles.ts
@@ -27,86 +27,133 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { newTimeseriesPainterFactory, newTimeseriesRowPaneFactory, TimelineTimeseriesPainterFactory } from './timeline_timeseries_row';
+import { newTimeseriesAnnotationPainterFactory } from './timeline_annotation_painter';
+import { newTimeseriesCursorPainterFactory } from './timeline_cursor_painter';
+import { TimelineRowPaneFactory } from './timeline_row';
+import { newEventsRowPaneFactory, newEventLimitsPainterFactory, newEventBarsPainterFactory, newEventIconsPainterFactory, newEventLabelsPainterFactory, newEventStripedBarsPainterFactory, newEventDashedBordersPainterFactory, JointType, newCombinedEventPainterFactory } from './timeline_events_row';
+import { TimelineRowModel } from './timeline_model';
+import { Color } from '../color';
+// Default
+//
- // Default
- //
+export let timeseriesRowPainterFactories_DEFAULT: TimelineTimeseriesPainterFactory[] = [
+ newTimeseriesPainterFactory(),
+ newTimeseriesAnnotationPainterFactory(),
+ newTimeseriesCursorPainterFactory({
+ font: '16px verdana,sans-serif',
+ buffer_px: 6
+ })
+];
- export var timeseriesRowPainterFactories_DEFAULT = [
- newTimeseriesPainterFactory( ),
- newTimeseriesAnnotationPainterFactory( ),
- newTimeseriesCursorPainterFactory( {
- font: '16px verdana,sans-serif',
- buffer_px: 6
- } )
- ];
-
- export var eventsRowPaneFactory_DEFAULT : TimelineRowPaneFactory = newEventsRowPaneFactory( {
- laneHeight: 40,
- painterFactories: [
- newEventLimitsPainterFactory( ),
- newEventBarsPainterFactory( ),
- newEventIconsPainterFactory( ),
- newEventLabelsPainterFactory( {
- iconsSizeFactor: 0.7
- } )
- ]
- } );
-
- export var timeseriesRowPaneFactory_DEFAULT : TimelineRowPaneFactory = newTimeseriesRowPaneFactory( {
- painterFactories: timeseriesRowPainterFactories_DEFAULT,
- axisOptions: { tickSpacing: 34 }
- } );
+export let eventsRowPaneFactory_DEFAULT: TimelineRowPaneFactory = newEventsRowPaneFactory({
+ laneHeight: 40,
+ painterFactories: [
+ newEventLimitsPainterFactory(),
+ newEventBarsPainterFactory(),
+ newEventIconsPainterFactory(),
+ newEventLabelsPainterFactory({
+ iconsSizeFactor: 0.7
+ })
+ ]
+});
- export function rowPaneFactoryChooser_DEFAULT( row : TimelineRowModel ) : TimelineRowPaneFactory {
- if ( !row.eventGuids.isEmpty ) {
- return eventsRowPaneFactory_DEFAULT;
- }
- else if ( !row.timeseriesGuids.isEmpty ) {
- return timeseriesRowPaneFactory_DEFAULT;
- }
- else {
- return null;
- }
+export let timeseriesRowPaneFactory_DEFAULT: TimelineRowPaneFactory = newTimeseriesRowPaneFactory({
+ painterFactories: timeseriesRowPainterFactories_DEFAULT,
+ axisOptions: { tickSpacing: 34 }
+});
+
+export function rowPaneFactoryChooser_DEFAULT(row: TimelineRowModel): TimelineRowPaneFactory {
+ if (!row.eventGuids.isEmpty) {
+ return eventsRowPaneFactory_DEFAULT;
+ }
+ else if (!row.timeseriesGuids.isEmpty) {
+ return timeseriesRowPaneFactory_DEFAULT;
+ }
+ else {
+ return null;
}
+}
- // Thin
- //
+// Thin
+//
- export var eventsRowPaneFactory_THIN : TimelineRowPaneFactory = newEventsRowPaneFactory( {
- rowTopPadding: 0,
- rowBottomPadding: 0,
- laneHeight: 23,
- allowMultipleLanes: true,
- painterFactories: [
- newEventLimitsPainterFactory( {
- lineColor: new Color( 1, 0, 0, 1 ),
- lineThickness: 2
- } ),
- newEventStripedBarsPainterFactory( {
- bottomMargin: 0,
- topMargin: 13,
- minimumVisibleWidth: 0,
- stripeSlant: -1,
- stripeSecondaryWidth: 10,
- stripeWidth: 10
- } ),
- newEventDashedBordersPainterFactory( {
+export let eventsRowPaneFactory_THIN: TimelineRowPaneFactory = newEventsRowPaneFactory({
+ rowTopPadding: 0,
+ rowBottomPadding: 0,
+ laneHeight: 23,
+ allowMultipleLanes: true,
+ painterFactories: [
+ newEventLimitsPainterFactory({
+ lineColor: new Color(1, 0, 0, 1),
+ lineThickness: 2
+ }),
+ newEventStripedBarsPainterFactory({
+ bottomMargin: 0,
+ topMargin: 13,
+ minimumVisibleWidth: 0,
+ stripeSlant: -1,
+ stripeSecondaryWidth: 10,
+ stripeWidth: 10
+ }),
+ newEventDashedBordersPainterFactory({
+ bottomMargin: 0,
+ topMargin: 13,
+ minimumVisibleWidth: 0,
+ cornerType: JointType.MITER,
+ dashLength: 5
+ }),
+ newEventIconsPainterFactory({
+ bottomMargin: 0,
+ topMargin: 13,
+ vAlign: 0.0
+ }),
+ newEventLabelsPainterFactory({
+ bottomMargin: 12,
+ topMargin: 0,
+ leftMargin: 2,
+ rightMargin: 2,
+ vAlign: 0.0,
+ spacing: 2,
+ extendBeyondBar: false,
+ textMode: 'truncate'
+ })
+ ]
+});
+
+export function rowPaneFactoryChooser_THIN(row: TimelineRowModel): TimelineRowPaneFactory {
+ if (!row.eventGuids.isEmpty) {
+ return eventsRowPaneFactory_THIN;
+ }
+ else if (!row.timeseriesGuids.isEmpty) {
+ return timeseriesRowPaneFactory_DEFAULT;
+ }
+ else {
+ return null;
+ }
+}
+
+export let eventsRowPaneFactory_SINGLE: TimelineRowPaneFactory = newEventsRowPaneFactory({
+ rowTopPadding: 0,
+ rowBottomPadding: 0,
+ laneHeight: 23,
+ allowMultipleLanes: false,
+ painterFactories: [
+ newEventLimitsPainterFactory({
+ lineColor: new Color(1, 0, 0, 1),
+ lineThickness: 2
+ }),
+ newCombinedEventPainterFactory(
+ {
bottomMargin: 0,
topMargin: 13,
minimumVisibleWidth: 0,
- cornerType: JointType.MITER,
- dashLength: 5
- } ),
- newEventIconsPainterFactory( {
- bottomMargin: 0,
- topMargin: 13,
- vAlign: 0.0
- } ),
- newEventLabelsPainterFactory( {
+ cornerType: JointType.MITER
+ },
+ {
bottomMargin: 12,
topMargin: 0,
leftMargin: 2,
@@ -114,69 +161,26 @@ module Webglimpse {
vAlign: 0.0,
spacing: 2,
extendBeyondBar: false,
- textMode: 'truncate'
- } )
- ]
- } );
+ textMode: 'show'
+ },
+ {
+ bottomMargin: 0,
+ topMargin: 13,
+ vAlign: 0.0
+ }
+ )
+ ]
+});
- export function rowPaneFactoryChooser_THIN( row : TimelineRowModel ) : TimelineRowPaneFactory {
- if ( !row.eventGuids.isEmpty ) {
- return eventsRowPaneFactory_THIN;
- }
- else if ( !row.timeseriesGuids.isEmpty ) {
- return timeseriesRowPaneFactory_DEFAULT;
- }
- else {
- return null;
- }
+export function rowPaneFactoryChooser_SINGLE(row: TimelineRowModel): TimelineRowPaneFactory {
+ if (!row.eventGuids.isEmpty) {
+ return eventsRowPaneFactory_SINGLE;
+ }
+ else if (!row.timeseriesGuids.isEmpty) {
+ return timeseriesRowPaneFactory_DEFAULT;
}
-
- export var eventsRowPaneFactory_SINGLE : TimelineRowPaneFactory = newEventsRowPaneFactory( {
- rowTopPadding: 0,
- rowBottomPadding: 0,
- laneHeight: 23,
- allowMultipleLanes: false,
- painterFactories: [
- newEventLimitsPainterFactory( {
- lineColor: new Color( 1, 0, 0, 1 ),
- lineThickness: 2
- } ),
- newCombinedEventPainterFactory(
- {
- bottomMargin: 0,
- topMargin: 13,
- minimumVisibleWidth: 0,
- cornerType: JointType.MITER
- },
- {
- bottomMargin: 12,
- topMargin: 0,
- leftMargin: 2,
- rightMargin: 2,
- vAlign: 0.0,
- spacing: 2,
- extendBeyondBar: false,
- textMode: 'show'
- },
- {
- bottomMargin: 0,
- topMargin: 13,
- vAlign: 0.0
- }
- )
- ]
- } );
-
- export function rowPaneFactoryChooser_SINGLE( row : TimelineRowModel ) : TimelineRowPaneFactory {
- if ( !row.eventGuids.isEmpty ) {
- return eventsRowPaneFactory_SINGLE;
- }
- else if ( !row.timeseriesGuids.isEmpty ) {
- return timeseriesRowPaneFactory_DEFAULT;
- }
- else {
- return null;
- }
+ else {
+ return null;
}
+}
-}
\ No newline at end of file
diff --git a/src/webglimpse/timeline/timeline_timeseries_row.ts b/src/webglimpse/timeline/timeline_timeseries_row.ts
index a776565..4c1a2b2 100644
--- a/src/webglimpse/timeline/timeline_timeseries_row.ts
+++ b/src/webglimpse/timeline/timeline_timeseries_row.ts
@@ -27,688 +27,768 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
-
- export interface TimelineTimeseriesPainterOptions {
- timelineFont : string;
- timelineFgColor : Color;
- timelineThickness : number;
- rowTopPadding : number;
- rowBottomPadding : number;
- }
-
- export interface TimelineTimeseriesPainterFactory {
- ( drawable : Drawable, timeAxis : TimeAxis1D, dataAxis : Axis1D, model : TimelineModel, rowModel : TimelineRowModel, ui : TimelineUi, options : TimelineTimeseriesPainterOptions ) : Painter;
- }
-
- export interface TimelineTimeseriesRowPaneOptions {
- rowHeight? : number;
- rowTopPadding? : number;
- rowBottomPadding? : number;
- axisOptions? : EdgeAxisPainterOptions;
- axisWidth? : number
- painterFactories? : TimelineTimeseriesPainterFactory[];
- }
-
- export function newTimeseriesRowPaneFactory( rowOptions? : TimelineTimeseriesRowPaneOptions ) : TimelineRowPaneFactory {
- return function( drawable : Drawable, timeAxis : TimeAxis1D, dataAxis : Axis1D, model : TimelineModel, row : TimelineRowModel, ui : TimelineUi, options : TimelineRowPaneOptions ) : Pane {
-
- var rowTopPadding = ( hasval( rowOptions ) && hasval( rowOptions.rowTopPadding ) ? rowOptions.rowTopPadding : 6 );
- var rowBottomPadding = ( hasval( rowOptions ) && hasval( rowOptions.rowBottomPadding ) ? rowOptions.rowBottomPadding : 6 );
- var axisWidth = ( hasval( rowOptions ) && hasval( rowOptions.axisWidth ) ? rowOptions.axisWidth : 60 );
- var painterFactories = ( hasval( rowOptions ) && hasval( rowOptions.painterFactories ) ? rowOptions.painterFactories : [] );
- var axisOptions = ( hasval( rowOptions ) && hasval( rowOptions.axisOptions ) ? rowOptions.axisOptions : {} );
-
- var keyPrefix = options.isMaximized ? 'maximized-' : '';
-
- var getRowHeight = function( ) {
- // maximized rows do not specifiy a height (they should fill available space)
- if ( options.isMaximized ) {
- return null;
- }
- // if the row has a custom row specified, use it
- else if ( hasval( row.rowHeight ) ) {
- return row.rowHeight;
- }
- // otherwise use the default for this RowPaneFactory
- else if ( hasval( rowOptions ) && hasval( rowOptions.rowHeight ) ) {
- return rowOptions.rowHeight;
- }
- // as a last resort use a hard coded default
- else {
- return 135;
- }
+import { Color, white, darker } from '../color';
+import { Drawable, Painter, Pane, Mask2D, PointerEvent, yFrac, isLeftMouseDown, Layout } from '../core';
+import { TimeAxis1D } from './time_axis';
+import { Axis1D, attachAxisMouseListeners1D, Axis2D } from '../plot/axis';
+import { TimelineModel, TimelineRowModel, TimelineAnnotationModel, TimelineTimeseriesFragmentModel, TimelineTimeseriesModel } from './timeline_model';
+import { TimelineUi, TimelineSelectionModel } from './timeline_ui';
+import { EdgeAxisPainterOptions, newEdgeAxisPainter } from '../plot/edge_axis_painter';
+import { TimelineRowPaneFactory, TimelineRowPaneOptions } from './timeline_row';
+import { fixedSize, Side, solid_FRAGSHADER, dash_FRAGSHADER, putQuadXys } from '../misc';
+import { BoundsUnmodifiable } from '../bounds';
+import { newColumnLayout } from '../layout/column_layout';
+import { newOverlayLayout } from '../layout/overlay_layout';
+import { TimelineAnnotationStyleUi } from './timeline_annotation_style';
+import { indexAtOrBefore, indexNearest } from '../util/sorted_arrays';
+import { concatLines, GL, ensureCapacityFloat32, hasval } from '../util/util';
+import { Program, UniformColor, UniformMatrix4f, Attribute, Uniform1f } from '../shader';
+import { newDynamicBuffer } from '../buffer';
+import { glOrthoAxis } from '../matrix';
+
+export interface TimelineTimeseriesPainterOptions {
+ timelineFont: string;
+ timelineFgColor: Color;
+ timelineThickness: number;
+ rowTopPadding: number;
+ rowBottomPadding: number;
+}
+
+export type TimelineTimeseriesPainterFactory = (drawable: Drawable, timeAxis: TimeAxis1D, dataAxis: Axis1D, model: TimelineModel, rowModel: TimelineRowModel, ui: TimelineUi, options: TimelineTimeseriesPainterOptions) => Painter;
+
+export interface TimelineTimeseriesRowPaneOptions {
+ rowHeight?: number;
+ rowTopPadding?: number;
+ rowBottomPadding?: number;
+ axisOptions?: EdgeAxisPainterOptions;
+ axisWidth?: number;
+ painterFactories?: TimelineTimeseriesPainterFactory[];
+}
+
+export function newTimeseriesRowPaneFactory(rowOptions?: TimelineTimeseriesRowPaneOptions): TimelineRowPaneFactory {
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, dataAxis: Axis1D, model: TimelineModel, row: TimelineRowModel, ui: TimelineUi, options: TimelineRowPaneOptions): Pane {
+
+ const rowTopPadding = (hasval(rowOptions) && hasval(rowOptions.rowTopPadding) ? rowOptions.rowTopPadding : 6);
+ const rowBottomPadding = (hasval(rowOptions) && hasval(rowOptions.rowBottomPadding) ? rowOptions.rowBottomPadding : 6);
+ const axisWidth = (hasval(rowOptions) && hasval(rowOptions.axisWidth) ? rowOptions.axisWidth : 60);
+ const painterFactories = (hasval(rowOptions) && hasval(rowOptions.painterFactories) ? rowOptions.painterFactories : []);
+ const axisOptions = (hasval(rowOptions) && hasval(rowOptions.axisOptions) ? rowOptions.axisOptions : {});
+
+ const keyPrefix = options.isMaximized ? 'maximized-' : '';
+
+ const getRowHeight = function () {
+ // maximized rows do not specifiy a height (they should fill available space)
+ if (options.isMaximized) {
+ return null;
}
-
- var rowHeight : number = getRowHeight( );
-
- var timelineFont = options.timelineFont;
- var timelineFgColor = options.timelineFgColor;
- var draggableEdgeWidth = options.draggableEdgeWidth;
- var snapToDistance = options.snapToDistance;
-
- var rowUi = ui.rowUi( row.rowGuid );
- var input = ui.input;
- var selection = ui.selection;
-
- if ( !hasval( axisOptions.font ) ) axisOptions.font = timelineFont;
- if ( !hasval( axisOptions.tickColor ) ) axisOptions.tickColor = timelineFgColor;
- if ( !hasval( axisOptions.textColor ) ) axisOptions.textColor = timelineFgColor;
- if ( !hasval( axisOptions.showLabel ) ) axisOptions.showLabel = true;
- if ( !hasval( axisOptions.shortenLabels ) ) axisOptions.shortenLabels = false;
-
- var redraw = function( ) {
- drawable.redraw( );
- };
-
- // setup pane for data (y) axis painter and mouse listener
- var yAxisPane = new Pane( { updatePrefSize: fixedSize( axisWidth, rowHeight ) } );
- dataAxis.limitsChanged.on( redraw );
- attachAxisMouseListeners1D( yAxisPane, dataAxis, true );
-
- // add listener to update the height of the row if the rowHeight attribute changes
- var updateRowHeight = function( ) {
- yAxisPane.layout = { updatePrefSize: fixedSize( axisWidth, getRowHeight( ) ) };
- };
- row.attrsChanged.on( updateRowHeight );
-
- var isDragMode : Mask2D = function( viewport : BoundsUnmodifiable, i : number, j : number ) : boolean {
- var fragment = getNearestFragment( viewport, i, j ).fragment;
- return hasval( fragment );
- };
-
- var rowContentPane = new Pane( newColumnLayout( ), true, isDragMode );
- var underlayPane = new Pane( newOverlayLayout( ), false );
- var overlayPane = new Pane( null, false );
-
- var painterOptions = { timelineFont: timelineFont, timelineFgColor: timelineFgColor, timelineThickness: 1, rowTopPadding: rowTopPadding, rowBottomPadding: rowBottomPadding };
- for ( var n = 0; n < painterFactories.length; n++ ) {
- var createPainter = painterFactories[ n ];
- rowContentPane.addPainter( createPainter( drawable, timeAxis, dataAxis, model, row, ui, painterOptions ) );
+ // if the row has a custom row specified, use it
+ else if (hasval(row.rowHeight)) {
+ return row.rowHeight;
}
-
- yAxisPane.addPainter( newEdgeAxisPainter( dataAxis, Side.RIGHT, axisOptions ) );
- rowContentPane.addPane( yAxisPane, 0 );
- underlayPane.addPane( rowContentPane, true );
- underlayPane.addPane( overlayPane, false );
-
- rowUi.addPane( keyPrefix+'content', rowContentPane );
- rowUi.addPane( keyPrefix+'overlay', overlayPane );
- rowUi.addPane( keyPrefix+'underlay', underlayPane );
- rowUi.addPane( keyPrefix+'y-axis', yAxisPane );
-
- row.timeseriesGuids.valueAdded.on( redraw );
- row.timeseriesGuids.valueMoved.on( redraw );
- row.timeseriesGuids.valueRemoved.on( redraw );
-
- var addFragmentRedraw = function( fragmentGuid : string ) {
- var fragment = model.timeseriesFragment( fragmentGuid );
- fragment.dataChanged.on( redraw );
+ // otherwise use the default for this RowPaneFactory
+ else if (hasval(rowOptions) && hasval(rowOptions.rowHeight)) {
+ return rowOptions.rowHeight;
}
-
- var removeFragmentRedraw = function( fragmentGuid : string ) {
- var fragment = model.timeseriesFragment( fragmentGuid );
- fragment.dataChanged.off( redraw );
+ // as a last resort use a hard coded default
+ else {
+ return 135;
}
-
- var addRedraw = function( timeseriesGuid : string ) {
- var timeseries = model.timeseries( timeseriesGuid );
- timeseries.attrsChanged.on( redraw );
- timeseries.fragmentGuids.valueAdded.on( redraw );
- timeseries.fragmentGuids.valueRemoved.on( redraw );
-
- timeseries.fragmentGuids.forEach( addFragmentRedraw );
- timeseries.fragmentGuids.valueAdded.on( addFragmentRedraw );
- timeseries.fragmentGuids.valueRemoved.on( removeFragmentRedraw );
- };
- row.timeseriesGuids.forEach( addRedraw );
- row.timeseriesGuids.valueAdded.on( addRedraw );
-
- var removeRedraw = function( timeseriesGuid : string ) {
- var timeseries = model.timeseries( timeseriesGuid );
- timeseries.attrsChanged.off( redraw );
- timeseries.fragmentGuids.valueAdded.off( redraw );
- timeseries.fragmentGuids.valueRemoved.off( redraw );
- timeseries.fragmentGuids.forEach( removeFragmentRedraw );
- };
- row.timeseriesGuids.valueRemoved.on( removeRedraw );
-
- var timeAtCoords_PMILLIS = function( viewport : BoundsUnmodifiable, i : number ) : number {
- return timeAxis.tAtFrac_PMILLIS( viewport.xFrac( i ) );
- };
-
- var timeAtPointer_PMILLIS = function( ev : PointerEvent ) : number {
- return timeAtCoords_PMILLIS( ev.paneViewport, ev.i );
- };
-
- // Used by both sets of listeners to know whether a timeseries-drag is in progress
- var timeseriesDragMode : string = null;
-
- // Hook up input notifications
- //
-
- var recentMouseMove : PointerEvent = null;
-
- rowContentPane.mouseMove.on( function( ev : PointerEvent ) {
- input.mouseMove.fire( ev );
- if ( !hasval( timeseriesDragMode ) ) {
- input.timeHover.fire( timeAtPointer_PMILLIS( ev ), ev );
- input.rowHover.fire( row, ev );
- }
- recentMouseMove = ev;
- } );
-
- rowContentPane.mouseExit.on( function( ev : PointerEvent ) {
- input.mouseExit.fire( ev );
- if ( !hasval( timeseriesDragMode ) ) {
- input.timeHover.fire( null, ev );
- input.rowHover.fire( null, ev );
- input.eventHover.fire( null, ev );
- }
- recentMouseMove = null;
- } );
+ };
- var uiMillisPerPxChanged = function( ) {
- if ( !hasval( timeseriesDragMode ) && recentMouseMove != null ) {
- var ev = recentMouseMove;
- input.timeHover.fire( timeAtPointer_PMILLIS( ev ), ev );
- }
- };
- ui.millisPerPx.changed.on( uiMillisPerPxChanged );
-
- rowContentPane.mouseUp.on( function( ev : PointerEvent ) {
- input.mouseUp.fire( ev );
- } );
-
- rowContentPane.mouseDown.on( function( ev : PointerEvent ) {
- input.mouseDown.fire( ev );
- } );
-
- rowContentPane.mouseWheel.on( options.mouseWheelListener );
-
- rowContentPane.contextMenu.on( function( ev : PointerEvent ) {
- input.contextMenu.fire( ev );
- } );
-
-
- // Begin annotation selection
- //
-
- var getNearestAnnotation = function( viewport : BoundsUnmodifiable, i : number, j : number ) {
- // maximum number of pixels away from a point the mouse can be to select it
- var pickBuffer_PIXEL : number = 10;
- // value per pixel in x and y directions
- var vppx : number = ui.millisPerPx.value;
- var vppy : number = dataAxis.vSize / rowContentPane.viewport.h;
- var pickBuffer_PMILLIS : number = pickBuffer_PIXEL * vppx;
-
- var ev_time : number = timeAtCoords_PMILLIS( viewport, i );
- var ev_value : number = dataAxis.vAtFrac( viewport.yFrac( j ) );
-
- var bestAnnotation : TimelineAnnotationModel = null;
- var best_PIXEL : number = null;
-
- if ( ev_time ) {
- for ( var i = 0 ; i < row.annotationGuids.length ; i++ ) {
- var annotationGuid : string = row.annotationGuids.valueAt( i );
- var annotation : TimelineAnnotationModel = model.annotation( annotationGuid );
- var styleGuid : string = annotation.styleGuid;
- var style : TimelineAnnotationStyleUi = ui.annotationStyle( styleGuid );
-
- var dy_PIXEL = Math.abs( annotation.y - ev_value ) / vppy;
- var dx_PIXEL = Math.abs( annotation.time_PMILLIS - ev_time ) / vppx;
-
- if ( style.uiHint == 'point' ) {
- var d_PIXEL = Math.sqrt( dx_PIXEL * dx_PIXEL + dy_PIXEL * dy_PIXEL );
- }
- else if ( style.uiHint == 'horizontal-line' ) {
- var d_PIXEL = dy_PIXEL;
- }
- else if ( style.uiHint == 'vertical-line' ) {
- var d_PIXEL = dx_PIXEL;
- }
-
- if ( d_PIXEL < pickBuffer_PIXEL ) {
- if ( !hasval( best_PIXEL ) || d_PIXEL < best_PIXEL ) {
- bestAnnotation = annotation;
- best_PIXEL = d_PIXEL;
- }
+ const rowHeight: number = getRowHeight();
+
+ const timelineFont = options.timelineFont;
+ const timelineFgColor = options.timelineFgColor;
+ const draggableEdgeWidth = options.draggableEdgeWidth;
+ const snapToDistance = options.snapToDistance;
+
+ const rowUi = ui.rowUi(row.rowGuid);
+ const input = ui.input;
+ const selection = ui.selection;
+
+ if (!hasval(axisOptions.font)) {
+ axisOptions.font = timelineFont;
+ }
+ if (!hasval(axisOptions.tickColor)) {
+ axisOptions.tickColor = timelineFgColor;
+ }
+ if (!hasval(axisOptions.textColor)) {
+ axisOptions.textColor = timelineFgColor;
+ }
+ if (!hasval(axisOptions.showLabel)) {
+ axisOptions.showLabel = true;
+ }
+ if (!hasval(axisOptions.shortenLabels)) {
+ axisOptions.shortenLabels = false;
+ }
+
+ const redraw = function () {
+ drawable.redraw();
+ };
+
+ // setup pane for data (y) axis painter and mouse listener
+ const yAxisPane = new Pane({ updatePrefSize: fixedSize(axisWidth, rowHeight) });
+ dataAxis.limitsChanged.on(redraw);
+ attachAxisMouseListeners1D(yAxisPane, dataAxis, true);
+
+ // add listener to update the height of the row if the rowHeight attribute changes
+ const updateRowHeight = function () {
+ yAxisPane.layout = { updatePrefSize: fixedSize(axisWidth, getRowHeight()) };
+ };
+ row.attrsChanged.on(updateRowHeight);
+
+ const isDragMode: Mask2D = function (viewport: BoundsUnmodifiable, i: number, j: number): boolean {
+ const fragment = getNearestFragment(viewport, i, j).fragment;
+ return hasval(fragment);
+ };
+
+ const rowContentPane = new Pane(newColumnLayout(), true, isDragMode);
+ const underlayPane = new Pane(newOverlayLayout(), false);
+ const overlayPane = new Pane(null, false);
+
+ const painterOptions = { timelineFont: timelineFont, timelineFgColor: timelineFgColor, timelineThickness: 1, rowTopPadding: rowTopPadding, rowBottomPadding: rowBottomPadding };
+ for (let n = 0; n < painterFactories.length; n++) {
+ const createPainter = painterFactories[n];
+ rowContentPane.addPainter(createPainter(drawable, timeAxis, dataAxis, model, row, ui, painterOptions));
+ }
+
+ yAxisPane.addPainter(newEdgeAxisPainter(dataAxis, Side.RIGHT, axisOptions));
+ rowContentPane.addPane(yAxisPane, 0);
+ underlayPane.addPane(rowContentPane, true);
+ underlayPane.addPane(overlayPane, false);
+
+ rowUi.addPane(keyPrefix + 'content', rowContentPane);
+ rowUi.addPane(keyPrefix + 'overlay', overlayPane);
+ rowUi.addPane(keyPrefix + 'underlay', underlayPane);
+ rowUi.addPane(keyPrefix + 'y-axis', yAxisPane);
+
+ row.timeseriesGuids.valueAdded.on(redraw);
+ row.timeseriesGuids.valueMoved.on(redraw);
+ row.timeseriesGuids.valueRemoved.on(redraw);
+
+ const addFragmentRedraw = function (fragmentGuid: string) {
+ const fragment = model.timeseriesFragment(fragmentGuid);
+ fragment.dataChanged.on(redraw);
+ };
+
+ const removeFragmentRedraw = function (fragmentGuid: string) {
+ const fragment = model.timeseriesFragment(fragmentGuid);
+ fragment.dataChanged.off(redraw);
+ };
+
+ const addRedraw = function (timeseriesGuid: string) {
+ const timeseries = model.timeseries(timeseriesGuid);
+ timeseries.attrsChanged.on(redraw);
+ timeseries.fragmentGuids.valueAdded.on(redraw);
+ timeseries.fragmentGuids.valueRemoved.on(redraw);
+
+ timeseries.fragmentGuids.forEach(addFragmentRedraw);
+ timeseries.fragmentGuids.valueAdded.on(addFragmentRedraw);
+ timeseries.fragmentGuids.valueRemoved.on(removeFragmentRedraw);
+ };
+ row.timeseriesGuids.forEach(addRedraw);
+ row.timeseriesGuids.valueAdded.on(addRedraw);
+
+ const removeRedraw = function (timeseriesGuid: string) {
+ const timeseries = model.timeseries(timeseriesGuid);
+ timeseries.attrsChanged.off(redraw);
+ timeseries.fragmentGuids.valueAdded.off(redraw);
+ timeseries.fragmentGuids.valueRemoved.off(redraw);
+ timeseries.fragmentGuids.forEach(removeFragmentRedraw);
+ };
+ row.timeseriesGuids.valueRemoved.on(removeRedraw);
+
+ const timeAtCoords_PMILLIS = function (viewport: BoundsUnmodifiable, i: number): number {
+ return timeAxis.tAtFrac_PMILLIS(viewport.xFrac(i));
+ };
+
+ const timeAtPointer_PMILLIS = function (ev: PointerEvent): number {
+ return timeAtCoords_PMILLIS(ev.paneViewport, ev.i);
+ };
+
+ // Used by both sets of listeners to know whether a timeseries-drag is in progress
+ let timeseriesDragMode: string = null;
+
+ // Hook up input notifications
+ //
+
+ let recentMouseMove: PointerEvent = null;
+
+ rowContentPane.mouseMove.on(function (ev: PointerEvent) {
+ input.mouseMove.fire(ev);
+ if (!hasval(timeseriesDragMode)) {
+ input.timeHover.fire(timeAtPointer_PMILLIS(ev), ev);
+ input.rowHover.fire(row, ev);
+ }
+ recentMouseMove = ev;
+ });
+
+ rowContentPane.mouseExit.on(function (ev: PointerEvent) {
+ input.mouseExit.fire(ev);
+ if (!hasval(timeseriesDragMode)) {
+ input.timeHover.fire(null, ev);
+ input.rowHover.fire(null, ev);
+ input.eventHover.fire(null, ev);
+ }
+ recentMouseMove = null;
+ });
+
+ const uiMillisPerPxChanged = function () {
+ if (!hasval(timeseriesDragMode) && recentMouseMove != null) {
+ const ev = recentMouseMove;
+ input.timeHover.fire(timeAtPointer_PMILLIS(ev), ev);
+ }
+ };
+ ui.millisPerPx.changed.on(uiMillisPerPxChanged);
+
+ rowContentPane.mouseUp.on(function (ev: PointerEvent) {
+ input.mouseUp.fire(ev);
+ });
+
+ rowContentPane.mouseDown.on(function (ev: PointerEvent) {
+ input.mouseDown.fire(ev);
+ });
+
+ rowContentPane.mouseWheel.on(options.mouseWheelListener);
+
+ rowContentPane.contextMenu.on(function (ev: PointerEvent) {
+ input.contextMenu.fire(ev);
+ });
+
+
+ // Begin annotation selection
+ //
+
+ const getNearestAnnotation = function (viewport: BoundsUnmodifiable, i: number, j: number) {
+ // maximum number of pixels away from a point the mouse can be to select it
+ const pickBuffer_PIXEL = 10;
+ // value per pixel in x and y directions
+ const vppx: number = ui.millisPerPx.value;
+ const vppy: number = dataAxis.vSize / rowContentPane.viewport.h;
+ const pickBuffer_PMILLIS: number = pickBuffer_PIXEL * vppx;
+
+ const ev_time: number = timeAtCoords_PMILLIS(viewport, i);
+ const ev_value: number = dataAxis.vAtFrac(viewport.yFrac(j));
+
+ let bestAnnotation: TimelineAnnotationModel = null;
+ let best_PIXEL: number = null;
+
+ if (ev_time) {
+ for (let annotationIdx = 0; annotationIdx < row.annotationGuids.length; annotationIdx++) {
+ const annotationGuid: string = row.annotationGuids.valueAt(annotationIdx);
+ const annotation: TimelineAnnotationModel = model.annotation(annotationGuid);
+ if (!annotation.pickable) {
+ continue;
+ }
+ const styleGuid: string = annotation.styleGuid;
+ const style: TimelineAnnotationStyleUi = ui.annotationStyle(styleGuid);
+
+ const dy_PIXEL = Math.abs(annotation.y - ev_value) / vppy;
+ const dx_PIXEL = Math.abs(annotation.time_PMILLIS - ev_time) / vppx;
+
+ let d_PIXEL = 0;
+ if (style.uiHint === 'point') {
+ d_PIXEL = Math.sqrt(dx_PIXEL * dx_PIXEL + dy_PIXEL * dy_PIXEL);
+ }
+ else if (style.uiHint === 'horizontal-line') {
+ d_PIXEL = dy_PIXEL;
+ }
+ else if (style.uiHint === 'vertical-line') {
+ d_PIXEL = dx_PIXEL;
+ }
+
+ if (d_PIXEL < pickBuffer_PIXEL) {
+ if (!hasval(best_PIXEL) || d_PIXEL < best_PIXEL) {
+ bestAnnotation = annotation;
+ best_PIXEL = d_PIXEL;
}
}
}
-
- return bestAnnotation;
}
-
- var getNearestAnnotationEvent = function( ev : PointerEvent ) {
- return getNearestAnnotation( ev.paneViewport, ev.i, ev.j );
+
+ return bestAnnotation;
+ };
+
+ const getNearestAnnotationEvent = function (ev: PointerEvent) {
+ return getNearestAnnotation(ev.paneViewport, ev.i, ev.j);
+ };
+
+ overlayPane.mouseMove.on(function (ev: PointerEvent) {
+ // update selection.hoveredYValue
+ const y: number = dataAxis.vAtFrac(yFrac(ev));
+ selection.hoveredY.value = y;
+
+ // update selection.hoveredAnnotation
+ const result = getNearestAnnotationEvent(ev);
+ selection.hoveredAnnotation.value = result;
+ });
+ selection.hoveredAnnotation.changed.on(redraw);
+
+ overlayPane.mouseExit.on(function () {
+ selection.hoveredY.value = undefined;
+ selection.hoveredAnnotation.value = null;
+ });
+
+ // Begin timeseries-drag
+ //
+
+ function chooseTimeseriesDragMode(timelineUi: TimelineUi, hoveredTimeseriesFragment: TimelineTimeseriesFragmentModel): string {
+ if (!hasval(hoveredTimeseriesFragment)) {
+ return null;
}
-
- overlayPane.mouseMove.on( function( ev : PointerEvent ) {
- // update selection.hoveredYValue
- var y : number = dataAxis.vAtFrac( yFrac( ev ) );
- selection.hoveredY.value = y;
-
- // update selection.hoveredAnnotation
- var result = getNearestAnnotationEvent( ev );
- selection.hoveredAnnotation.value = result;
- } );
- selection.hoveredAnnotation.changed.on( redraw );
-
- overlayPane.mouseExit.on( function( ) {
- selection.hoveredY.value = undefined;
- selection.hoveredAnnotation.value = null;
- } );
-
- // Begin timeseries-drag
- //
-
- function chooseTimeseriesDragMode( ui : TimelineUi, hoveredTimeseriesFragment : TimelineTimeseriesFragmentModel ) : string {
- if ( !hasval( hoveredTimeseriesFragment ) ) {
- return null;
- }
- // return the edit mode of the selected fragment
- else {
- return hoveredTimeseriesFragment.userEditMode;
- }
+ // return the edit mode of the selected fragment
+ else {
+ return hoveredTimeseriesFragment.userEditMode;
}
-
- var updateCursor = function( ) {
- if ( !timeseriesDragMode ) {
- var mouseCursors = { 'xy': 'move', 'y': 'ns-resize' };
- rowContentPane.mouseCursor = mouseCursors[ chooseTimeseriesDragMode( ui, selection.hoveredTimeseries.fragment ) ];
- }
- };
- ui.millisPerPx.changed.on( updateCursor );
- selection.hoveredTimeseries.changed.on( updateCursor );
-
- var getNearestFragment = function( viewport : BoundsUnmodifiable, i : number, j : number ) {
- // maximum number of pixels away from a point the mouse can be to select it
- var pickBuffer_PIXEL : number = 10;
- // value per pixel in x and y directions
- var vppx : number = ui.millisPerPx.value;
- var vppy : number = dataAxis.vSize / rowContentPane.viewport.h;
- var pickBuffer_PMILLIS : number = pickBuffer_PIXEL * vppx;
-
- var bestFragment : TimelineTimeseriesFragmentModel;
- var bestIndex : number;
- var best_PIXEL : number;
-
- var ev_time : number = timeAtCoords_PMILLIS( viewport, i );
- var ev_value : number = dataAxis.vAtFrac( viewport.yFrac( j ) );
-
- if ( ev_time ) {
- for ( var i = 0 ; i < row.timeseriesGuids.length ; i++ ) {
- var timeseriesGuid : string = row.timeseriesGuids.valueAt( i );
- var timeseries : TimelineTimeseriesModel = model.timeseries( timeseriesGuid );
-
- for ( var j = 0 ; j < timeseries.fragmentGuids.length ; j++ ) {
- var fragmentGuid : string = timeseries.fragmentGuids.valueAt( j );
- var fragment : TimelineTimeseriesFragmentModel = model.timeseriesFragment( fragmentGuid );
-
- // fragments should not overlap
- if ( fragment.start_PMILLIS - pickBuffer_PMILLIS < ev_time && fragment.end_PMILLIS + pickBuffer_PMILLIS > ev_time ) {
- // bars are drawn starting at the point and continuing to the next point, so we need to choose the closest index differently
- var index : number = timeseries.uiHint == 'bars' ? indexAtOrBefore( fragment.times_PMILLIS, ev_time ) : indexNearest( fragment.times_PMILLIS, ev_time );
- var value = fragment.data[index];
- var time = fragment.times_PMILLIS[index];
-
- var dy_PIXEL = ( value - ev_value ) / vppy;
- var dx_PIXEL = ( time - ev_time ) / vppx;
- var d_PIXEL = Math.sqrt( dx_PIXEL * dx_PIXEL + dy_PIXEL * dy_PIXEL );
-
- var filter = function( ) : boolean {
- if ( timeseries.uiHint == 'bars' ) {
- return ( timeseries.baseline < ev_value && ev_value < value ) ||
- ( timeseries.baseline > ev_value && ev_value > value );
- }
- else {
- return d_PIXEL < pickBuffer_PIXEL;
- }
+ }
+
+ const updateCursor = function () {
+ if (!timeseriesDragMode) {
+ const mouseCursors = { 'xy': 'move', 'y': 'ns-resize' };
+ rowContentPane.mouseCursor = mouseCursors[chooseTimeseriesDragMode(ui, selection.hoveredTimeseries.fragment)];
+ }
+ };
+ ui.millisPerPx.changed.on(updateCursor);
+ selection.hoveredTimeseries.changed.on(updateCursor);
+
+ const getNearestFragment = function (viewport: BoundsUnmodifiable, i: number, j: number) {
+ // maximum number of pixels away from a point the mouse can be to select it
+ const pickBuffer_PIXEL = 10;
+ // value per pixel in x and y directions
+ const vppx: number = ui.millisPerPx.value;
+ const vppy: number = dataAxis.vSize / rowContentPane.viewport.h;
+ const pickBuffer_PMILLIS: number = pickBuffer_PIXEL * vppx;
+
+ let bestFragment: TimelineTimeseriesFragmentModel;
+ let bestIndex: number;
+ let best_PIXEL: number;
+
+ const ev_time: number = timeAtCoords_PMILLIS(viewport, i);
+ const ev_value: number = dataAxis.vAtFrac(viewport.yFrac(j));
+
+ if (ev_time) {
+ for (let timeseriesIdx = 0; timeseriesIdx < row.timeseriesGuids.length; timeseriesIdx++) {
+ const timeseriesGuid: string = row.timeseriesGuids.valueAt(timeseriesIdx);
+ const timeseries: TimelineTimeseriesModel = model.timeseries(timeseriesGuid);
+
+ for (let fragmentIdx = 0; fragmentIdx < timeseries.fragmentGuids.length; fragmentIdx++) {
+ const fragmentGuid: string = timeseries.fragmentGuids.valueAt(fragmentIdx);
+ const fragment: TimelineTimeseriesFragmentModel = model.timeseriesFragment(fragmentGuid);
+
+ // fragments should not overlap
+ if (fragment.start_PMILLIS - pickBuffer_PMILLIS < ev_time && fragment.end_PMILLIS + pickBuffer_PMILLIS > ev_time) {
+ // bars are drawn starting at the point and continuing to the next point, so we need to choose the closest index differently
+ const index: number = timeseries.uiHint === 'bars' ? indexAtOrBefore(fragment.times_PMILLIS, ev_time) : indexNearest(fragment.times_PMILLIS, ev_time);
+ const value = fragment.data[index];
+ const time = fragment.times_PMILLIS[index];
+
+ const dy_PIXEL = (value - ev_value) / vppy;
+ const dx_PIXEL = (time - ev_time) / vppx;
+ const d_PIXEL = Math.sqrt(dx_PIXEL * dx_PIXEL + dy_PIXEL * dy_PIXEL);
+
+ const filter = function (): boolean {
+ if (timeseries.uiHint === 'bars') {
+ return (timeseries.baseline < ev_value && ev_value < value) ||
+ (timeseries.baseline > ev_value && ev_value > value);
}
-
- if ( ( !best_PIXEL || d_PIXEL < best_PIXEL ) && filter( ) )
- {
- best_PIXEL = d_PIXEL;
- bestFragment = fragment;
- bestIndex = index;
+ else {
+ return d_PIXEL < pickBuffer_PIXEL;
}
+ };
+
+ if ((!best_PIXEL || d_PIXEL < best_PIXEL) && filter()) {
+ best_PIXEL = d_PIXEL;
+ bestFragment = fragment;
+ bestIndex = index;
}
}
}
}
-
- return { fragment: bestFragment, index: bestIndex };
- };
-
- var getNearestFragmentEvent = function( ev : PointerEvent ) {
- return getNearestFragment( ev.paneViewport, ev.i, ev.j );
}
-
- // choose the closest data point to the mouse cursor position and fire an event when it changes
- rowContentPane.mouseMove.on( function( ev : PointerEvent ) {
- if ( ! hasval( timeseriesDragMode ) ) {
- var result = getNearestFragmentEvent( ev );
- selection.hoveredTimeseries.setValue( result.fragment, result.index );
- }
- } );
- selection.hoveredTimeseries.changed.on( redraw );
-
- rowContentPane.mouseExit.on( function( ) {
- selection.hoveredTimeseries.clearValue( );
- } );
-
- rowContentPane.mouseDown.on( function( ev : PointerEvent ) {
- if ( isLeftMouseDown( ev.mouseEvent ) ) {
- timeseriesDragMode = chooseTimeseriesDragMode( ui, selection.hoveredTimeseries.fragment );
+
+ return { fragment: bestFragment, index: bestIndex };
+ };
+
+ const getNearestFragmentEvent = function (ev: PointerEvent) {
+ return getNearestFragment(ev.paneViewport, ev.i, ev.j);
+ };
+
+ // choose the closest data point to the mouse cursor position and fire an event when it changes
+ rowContentPane.mouseMove.on(function (ev: PointerEvent) {
+ if (!hasval(timeseriesDragMode)) {
+ const result = getNearestFragmentEvent(ev);
+ selection.hoveredTimeseries.setValue(result.fragment, result.index);
+ }
+ });
+ selection.hoveredTimeseries.changed.on(redraw);
+
+ rowContentPane.mouseExit.on(function () {
+ selection.hoveredTimeseries.clearValue();
+ });
+
+ rowContentPane.mouseDown.on(function (ev: PointerEvent) {
+ if (isLeftMouseDown(ev.mouseEvent)) {
+ timeseriesDragMode = chooseTimeseriesDragMode(ui, selection.hoveredTimeseries.fragment);
+ }
+ });
+
+ rowContentPane.mouseMove.on(function (ev: PointerEvent) {
+
+ if (hasval(timeseriesDragMode)) {
+ const x: number = timeAtPointer_PMILLIS(ev);
+ const y: number = dataAxis.vAtFrac(yFrac(ev));
+
+ const fragment = selection.hoveredTimeseries.fragment;
+ const fragment_time = fragment.times_PMILLIS;
+
+ if (timeseriesDragMode === 'y') {
+ fragment.setData(selection.hoveredTimeseries.index, y);
}
- } );
-
- rowContentPane.mouseMove.on( function( ev : PointerEvent ) {
-
- if ( hasval( timeseriesDragMode ) ) {
- var x : number = timeAtPointer_PMILLIS( ev );
- var y : number = dataAxis.vAtFrac( yFrac( ev ) );
-
- var fragment = selection.hoveredTimeseries.fragment;
- var fragment_time = fragment.times_PMILLIS;
-
- if ( timeseriesDragMode === 'y' ) {
- fragment.setData( selection.hoveredTimeseries.index, y );
- }
- else if ( timeseriesDragMode === 'xy' ) {
- var index = fragment.setData( selection.hoveredTimeseries.index, y, x );
- if ( index !== selection.hoveredTimeseries.index ) {
- selection.hoveredTimeseries.setValue( fragment, index );
- }
+ else if (timeseriesDragMode === 'xy') {
+ const index = fragment.setData(selection.hoveredTimeseries.index, y, x);
+ if (index !== selection.hoveredTimeseries.index) {
+ selection.hoveredTimeseries.setValue(fragment, index);
}
}
- } );
-
- // Finish event-drag
- //
-
- rowContentPane.mouseUp.on( function( ev : PointerEvent ) {
- timeseriesDragMode = null;
- } );
-
- rowContentPane.dispose.on( function( ) {
-
- rowUi.removePane( keyPrefix+'content' );
- rowUi.removePane( keyPrefix+'overlay' );
- rowUi.removePane( keyPrefix+'underlay' );
- rowUi.removePane( keyPrefix+'y-axis' );
-
- dataAxis.limitsChanged.off( redraw );
-
- row.timeseriesGuids.valueAdded.off( redraw );
- row.timeseriesGuids.valueMoved.off( redraw );
- row.timeseriesGuids.valueRemoved.off( redraw );
-
- row.timeseriesGuids.valueAdded.off( addRedraw );
- row.timeseriesGuids.valueRemoved.off( removeRedraw );
-
- selection.hoveredTimeseries.changed.off( redraw );
-
- row.attrsChanged.off( updateRowHeight );
-
- row.timeseriesGuids.forEach( function( timeseriesGuid : string ) {
- var timeseries = model.timeseries( timeseriesGuid );
- timeseries.attrsChanged.off( redraw );
- timeseries.fragmentGuids.valueAdded.off( redraw );
- timeseries.fragmentGuids.valueRemoved.off( redraw );
- } );
- } );
-
- return underlayPane;
- }
- }
-
- export function newTimeseriesPainterFactory( options? : TimelineTimeseriesPainterOptions ) : TimelineTimeseriesPainterFactory {
- // Painter Factory
- return function( drawable : Drawable, timeAxis : TimeAxis1D, dataAxis : Axis1D, model : TimelineModel, rowModel : TimelineRowModel, ui : TimelineUi ) : Painter {
-
- var selection : TimelineSelectionModel = ui.selection;
- var defaultColor = hasval( options ) && hasval( options.timelineFgColor ) ? options.timelineFgColor : white;
- var defaultThickness = hasval( options ) && hasval( options.timelineThickness ) ? options.timelineThickness : 1;
-
- var modelview_pointsize_VERTSHADER = concatLines(
- ' uniform mat4 u_modelViewMatrix; ',
- ' attribute vec4 a_Position; ',
- ' uniform float u_PointSize; ',
- ' ',
- ' void main( ) { ',
- ' gl_PointSize = u_PointSize ; ',
- ' gl_Position = u_modelViewMatrix * a_Position ; ',
- ' } ',
- ' '
- );
-
- var program = new Program( modelview_pointsize_VERTSHADER, solid_FRAGSHADER );
- var u_Color = new UniformColor( program, 'u_Color' );
- var u_modelViewMatrix = new UniformMatrix4f( program, 'u_modelViewMatrix' );
- var a_Position = new Attribute( program, 'a_Position' );
- var u_PointSize = new Uniform1f( program, 'u_PointSize' );
-
- var axis = new Axis2D( timeAxis, dataAxis );
-
- var xys = new Float32Array( 0 );
- var xysBuffer = newDynamicBuffer( );
-
- // Painter
- return function( gl : WebGLRenderingContext, viewport : BoundsUnmodifiable ) {
-
- gl.blendFuncSeparate( GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA );
- gl.enable( GL.BLEND );
-
- // enable the shader
- program.use( gl );
-
- u_modelViewMatrix.setData( gl, glOrthoAxis( axis ) );
-
- for ( var i = 0 ; i < rowModel.timeseriesGuids.length ; i++ ) {
-
- // collect fragments and sort them by time
- var totalSize = 0;
- var sortedFragments = new Array( );
- var timeseriesGuid = rowModel.timeseriesGuids.valueAt(i);
- var timeseries = model.timeseries( timeseriesGuid );
- for ( var j = 0 ; j < timeseries.fragmentGuids.length ; j++ ) {
- var timeseriesFragmentGuid = timeseries.fragmentGuids.valueAt(j);
- var fragment = model.timeseriesFragment( timeseriesFragmentGuid );
-
- sortedFragments.push( fragment );
- totalSize += fragment.times_PMILLIS.length;
- }
- sortedFragments.sort( ( a,b ) => { return a.start_PMILLIS - b.start_PMILLIS; } );
-
- if ( timeseries.uiHint == 'lines' || timeseries.uiHint == 'points' || timeseries.uiHint == 'lines-and-points' || timeseries.uiHint == undefined ) {
-
- var size = totalSize * 2;
-
- xys = ensureCapacityFloat32( xys, size );
-
- var index = 0;
- for ( var j = 0 ; j < sortedFragments.length ; j++ ) {
- var fragment = sortedFragments[j];
- var data : number[] = fragment.data;
- var times_PMILLIS : number[] = fragment.times_PMILLIS;
-
- for ( var k = 0 ; k < data.length ; k++,index+=2 ) {
- xys[index] = timeAxis.vAtTime( times_PMILLIS[k] );
- xys[index+1] = data[k];
+ }
+ });
+
+ // Finish event-drag
+ //
+
+ rowContentPane.mouseUp.on(function (ev: PointerEvent) {
+ timeseriesDragMode = null;
+ });
+
+ rowContentPane.dispose.on(function () {
+
+ rowUi.removePane(keyPrefix + 'content');
+ rowUi.removePane(keyPrefix + 'overlay');
+ rowUi.removePane(keyPrefix + 'underlay');
+ rowUi.removePane(keyPrefix + 'y-axis');
+
+ dataAxis.limitsChanged.off(redraw);
+
+ row.timeseriesGuids.valueAdded.off(redraw);
+ row.timeseriesGuids.valueMoved.off(redraw);
+ row.timeseriesGuids.valueRemoved.off(redraw);
+
+ row.timeseriesGuids.valueAdded.off(addRedraw);
+ row.timeseriesGuids.valueRemoved.off(removeRedraw);
+
+ selection.hoveredTimeseries.changed.off(redraw);
+
+ row.attrsChanged.off(updateRowHeight);
+
+ row.timeseriesGuids.forEach(function (timeseriesGuid: string) {
+ const timeseries = model.timeseries(timeseriesGuid);
+ timeseries.attrsChanged.off(redraw);
+ timeseries.fragmentGuids.valueAdded.off(redraw);
+ timeseries.fragmentGuids.valueRemoved.off(redraw);
+ });
+ });
+
+ return underlayPane;
+ };
+}
+
+export function newTimeseriesPainterFactory(options?: TimelineTimeseriesPainterOptions): TimelineTimeseriesPainterFactory {
+ // Painter Factory
+ return function (drawable: Drawable, timeAxis: TimeAxis1D, dataAxis: Axis1D, model: TimelineModel, rowModel: TimelineRowModel, ui: TimelineUi): Painter {
+
+ const selection: TimelineSelectionModel = ui.selection;
+ const defaultColor = hasval(options) && hasval(options.timelineFgColor) ? options.timelineFgColor : white;
+ const defaultThickness = hasval(options) && hasval(options.timelineThickness) ? options.timelineThickness : 1;
+
+ const modelview_line_VERTSHADER = concatLines(
+ ' uniform mat4 u_modelViewMatrix; ',
+ ' attribute vec4 a_Position; ',
+ ' attribute float a_Distance; ',
+ ' varying float v_Distance; ',
+ ' uniform float u_PointSize; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_PointSize = u_PointSize ; ',
+ ' gl_Position = u_modelViewMatrix * a_Position ; ',
+ ' v_Distance = a_Distance; ',
+ ' } ',
+ ' '
+ );
+
+ const modelview_pointsize_VERTSHADER = concatLines(
+ ' uniform mat4 u_modelViewMatrix; ',
+ ' attribute vec4 a_Position; ',
+ ' uniform float u_PointSize; ',
+ ' ',
+ ' void main( ) { ',
+ ' gl_PointSize = u_PointSize ; ',
+ ' gl_Position = u_modelViewMatrix * a_Position ; ',
+ ' } ',
+ ' '
+ );
+
+ const program = new Program(modelview_pointsize_VERTSHADER, solid_FRAGSHADER);
+
+ const u_Color = new UniformColor(program, 'u_Color');
+ const u_modelViewMatrix = new UniformMatrix4f(program, 'u_modelViewMatrix');
+ const a_Position = new Attribute(program, 'a_Position');
+ const u_PointSize = new Uniform1f(program, 'u_PointSize');
+
+ const lineProgram = new Program(modelview_line_VERTSHADER, dash_FRAGSHADER);
+ const u_lpColor = new UniformColor(lineProgram, 'u_Color');
+ const u_lpmodelViewMatrix = new UniformMatrix4f(lineProgram, 'u_modelViewMatrix');
+ const a_lpPosition = new Attribute(lineProgram, 'a_Position');
+ const u_lpPointSize = new Uniform1f(lineProgram, 'u_PointSize');
+ const a_lpDistance = new Attribute(lineProgram, 'a_Distance');
+ const u_lpDash = new Uniform1f(lineProgram, 'u_Dash');
+
+ const axis = new Axis2D(timeAxis, dataAxis);
+
+ let xys = new Float32Array(0);
+ let dists = new Float32Array(0);
+ const xysBuffer = newDynamicBuffer();
+ const distBuffer = newDynamicBuffer();
+
+ // Painter
+ return function (gl: WebGLRenderingContext, viewport: BoundsUnmodifiable) {
+
+ gl.blendFuncSeparate(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA, GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
+ gl.enable(GL.BLEND);
+
+ let baseline = 0;
+
+ for (let i = 0; i < rowModel.timeseriesGuids.length; i++) {
+ // collect fragments and sort them by time
+ let totalSize = 0;
+ const sortedFragments = new Array();
+ const timeseriesGuid = rowModel.timeseriesGuids.valueAt(i);
+ const timeseries = model.timeseries(timeseriesGuid);
+ for (let j = 0; j < timeseries.fragmentGuids.length; j++) {
+ const timeseriesFragmentGuid = timeseries.fragmentGuids.valueAt(j);
+ const fragment = model.timeseriesFragment(timeseriesFragmentGuid);
+
+ sortedFragments.push(fragment);
+ totalSize += fragment.times_PMILLIS.length;
+ }
+ sortedFragments.sort((a, b) => a.start_PMILLIS - b.start_PMILLIS);
+
+ if (timeseries.uiHint === 'lines' || timeseries.uiHint === 'points' || timeseries.uiHint === 'lines-and-points' || timeseries.uiHint === undefined) {
+
+ // enable the shader
+ lineProgram.use(gl);
+
+ u_lpmodelViewMatrix.setData(gl, glOrthoAxis(axis));
+ const size = totalSize * 2;
+
+ xys = ensureCapacityFloat32(xys, size);
+ dists = ensureCapacityFloat32(dists, totalSize);
+
+ let index = 0;
+ for (let j = 0; j < sortedFragments.length; j++) {
+ const fragment = sortedFragments[j];
+ const data: number[] = fragment.data;
+ const times_PMILLIS: number[] = fragment.times_PMILLIS;
+
+ for (let k = 0; k < data.length; k++ , index += 2) {
+ xys[index] = timeAxis.vAtTime(times_PMILLIS[k]);
+ xys[index + 1] = data[k];
+ if (k !== 0) {
+ const x = (timeAxis.vAtTime(times_PMILLIS[k]) - timeAxis.vAtTime(times_PMILLIS[k - 1]));
+ const y = data[k] - data[k - 1];
+ dists[k] = dists[k - 1] + Math.sqrt(x * x + y * y);
}
- }
-
- var lineColor = hasval( timeseries.lineColor ) ? timeseries.lineColor : defaultColor;
- u_Color.setData( gl, lineColor );
-
- var lineThickness = hasval( timeseries.lineThickness ) ? timeseries.lineThickness : defaultThickness;
- gl.lineWidth( lineThickness );
-
- xysBuffer.setData( xys.subarray( 0, index ) );
- a_Position.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
-
- if ( timeseries.uiHint == 'lines' || timeseries.uiHint == 'lines-and-points' || timeseries.uiHint == undefined ) {
- // draw the lines
- gl.drawArrays( GL.LINE_STRIP, 0, size / 2 );
- }
-
- // point size works in WebKit and actually works in Minefield as well even though
- // VERTEX_PROGRAM_POINT_SIZE and POINT_SMOOTH aren't defined
- if ( timeseries.uiHint == 'points' || timeseries.uiHint == 'lines-and-points' ) {
-
- var pointColor = hasval( timeseries.pointColor ) ? timeseries.pointColor : defaultColor;
- u_Color.setData( gl, pointColor );
-
- u_PointSize.setData( gl, timeseries.pointSize );
-
- gl.drawArrays( GL.POINTS, 0, size / 2 );
- }
- }
- else if ( timeseries.uiHint == 'bars' ) {
-
- // The last data point defines the right edge of the bar
- // but it does not have its own bar drawn, so we need at
- // least 2 data points to draw any bars
- if ( totalSize >= 2 ) {
- var baseline : number = timeseries.baseline;
-
- var size = (totalSize-1) * 12;
- xys = ensureCapacityFloat32( xys, size );
-
- var index = 0;
- for ( var j = 0 ; j < sortedFragments.length ; j++ ) {
- var fragment = sortedFragments[j];
- var data : number[] = fragment.data;
- var times_PMILLIS : number[] = fragment.times_PMILLIS;
-
- for ( var k = 0 ; k < data.length-1 ; k++ ) {
- var x1 = timeAxis.vAtTime( times_PMILLIS[k] );
- var y1 = data[k];
-
- var x2 = timeAxis.vAtTime( times_PMILLIS[k+1] );
- var y2 = data[k+1];
-
- index = putQuadXys( xys, index, x1, x2, y1, baseline );
- }
+ else {
+ dists[k] = 0;
}
-
- var lineColor = hasval( timeseries.lineColor ) ? timeseries.lineColor : defaultColor;
- u_Color.setData( gl, lineColor );
-
- xysBuffer.setData( xys.subarray( 0, index ) );
- a_Position.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
-
- gl.drawArrays( GL.TRIANGLES, 0, size / 2 );
}
}
- else if ( timeseries.uiHint == 'area' ) {
-
- var baseline : number = timeseries.baseline;
-
- var size = totalSize * 4;
-
- // the last data point defines the right edge of the bar
- // but it does not have its own bar drawn
- xys = ensureCapacityFloat32( xys, size );
-
- var index = 0;
- for ( var j = 0 ; j < sortedFragments.length ; j++ ) {
- var fragment = sortedFragments[j];
- var data : number[] = fragment.data;
- var times_PMILLIS : number[] = fragment.times_PMILLIS;
-
- for ( var k = 0 ; k < data.length ; k++,index+=4 ) {
- var x1 = timeAxis.vAtTime( times_PMILLIS[k] );
- var y1 = data[k];
-
- xys[index] = x1;
- xys[index+1] = baseline;
-
- xys[index+2] = x1;
- xys[index+3] = y1;
+
+ const lineColor = hasval(timeseries.lineColor) ? timeseries.lineColor : defaultColor;
+ u_lpColor.setData(gl, lineColor);
+
+ const dash = hasval(timeseries.dash) ? timeseries.dash : 0;
+ u_lpDash.setData(gl, dash);
+
+ const lineThickness = hasval(timeseries.lineThickness) ? timeseries.lineThickness : defaultThickness;
+ gl.lineWidth(lineThickness);
+
+ xysBuffer.setData(xys.subarray(0, index));
+ a_lpPosition.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+
+ distBuffer.setData(dists.subarray(0, totalSize));
+ a_lpDistance.setDataAndEnable(gl, distBuffer, 1, GL.FLOAT);
+
+ if (timeseries.uiHint === 'lines' || timeseries.uiHint === 'lines-and-points' || timeseries.uiHint === undefined) {
+ // draw the lines
+ gl.drawArrays(GL.LINE_STRIP, 0, size / 2);
+ }
+
+ // point size works in WebKit and actually works in Minefield as well even though
+ // VERTEX_PROGRAM_POINT_SIZE and POINT_SMOOTH aren't defined
+ if (timeseries.uiHint === 'points' || timeseries.uiHint === 'lines-and-points') {
+
+ const pointColor = hasval(timeseries.pointColor) ? timeseries.pointColor : defaultColor;
+ u_Color.setData(gl, pointColor);
+
+ u_PointSize.setData(gl, timeseries.pointSize);
+
+ gl.drawArrays(GL.POINTS, 0, size / 2);
+ }
+ lineProgram.endUse(gl);
+ }
+ else if (timeseries.uiHint === 'bars') {
+ // enable the shader
+ program.use(gl);
+ u_modelViewMatrix.setData(gl, glOrthoAxis(axis));
+ // The last data point defines the right edge of the bar
+ // but it does not have its own bar drawn, so we need at
+ // least 2 data points to draw any bars
+ if (totalSize >= 2) {
+ baseline = timeseries.baseline;
+
+ const size = (totalSize - 1) * 12;
+ xys = ensureCapacityFloat32(xys, size);
+
+ let index = 0;
+ for (let j = 0; j < sortedFragments.length; j++) {
+ const fragment = sortedFragments[j];
+ const data: number[] = fragment.data;
+ const times_PMILLIS: number[] = fragment.times_PMILLIS;
+
+ for (let k = 0; k < data.length - 1; k++) {
+ const x1 = timeAxis.vAtTime(times_PMILLIS[k]);
+ const y1 = data[k];
+
+ const x2 = timeAxis.vAtTime(times_PMILLIS[k + 1]);
+ const y2 = data[k + 1];
+
+ index = putQuadXys(xys, index, x1, x2, y1, baseline);
}
}
-
- var lineColor = hasval( timeseries.lineColor ) ? timeseries.lineColor : defaultColor;
- u_Color.setData( gl, lineColor );
-
- xysBuffer.setData( xys.subarray( 0, index ) );
- a_Position.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
-
- gl.drawArrays( GL.TRIANGLE_STRIP, 0, size / 2 );
-
+
+ const lineColor = hasval(timeseries.lineColor) ? timeseries.lineColor : defaultColor;
+ u_Color.setData(gl, lineColor);
+
+ xysBuffer.setData(xys.subarray(0, index));
+ a_Position.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+
+ gl.drawArrays(GL.TRIANGLES, 0, size / 2);
}
-
- // highlight hovered point
- if ( selection.hoveredTimeseries.fragment && timeseries.fragmentGuids.hasValue( selection.hoveredTimeseries.fragment.fragmentGuid ) )
- {
- if ( timeseries.uiHint == 'area' || timeseries.uiHint == 'lines' || timeseries.uiHint == 'points' || timeseries.uiHint == 'lines-and-points' || timeseries.uiHint == undefined ) {
- var size = 8;
- xys = ensureCapacityFloat32( xys, size );
-
- var vppx : number = timeAxis.vSize / viewport.w;
- var vppy : number = dataAxis.vSize / viewport.h;
-
- var highlightSize = hasval( timeseries.pointSize ) ? timeseries.pointSize : 5;
-
- var bufferx = ( highlightSize / 2 ) * vppx;
- var buffery = ( highlightSize / 2 ) * vppy;
-
- var fragment = selection.hoveredTimeseries.fragment;
- var y = selection.hoveredTimeseries.data;
- var x = timeAxis.vAtTime( selection.hoveredTimeseries.times_PMILLIS );
-
- xys[0] = x-bufferx; xys[1] = y-buffery;
- xys[2] = x+bufferx; xys[3] = y-buffery;
- xys[4] = x+bufferx; xys[5] = y+buffery;
- xys[6] = x-bufferx; xys[7] = y+buffery;
-
- var color = hasval( timeseries.pointColor ) ? timeseries.pointColor : defaultColor;
- u_Color.setData( gl, darker( color, 0.8 ) );
-
- xysBuffer.setData( xys.subarray( 0, size ) );
- a_Position.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
-
- gl.drawArrays( GL.LINE_LOOP, 0, size / 2 );
+ program.endUse(gl);
+ }
+ else if (timeseries.uiHint === 'area') {
+ // enable the shader
+ program.use(gl);
+ u_modelViewMatrix.setData(gl, glOrthoAxis(axis));
+
+ baseline = timeseries.baseline;
+
+ const size = totalSize * 4;
+
+ // the last data point defines the right edge of the bar
+ // but it does not have its own bar drawn
+ xys = ensureCapacityFloat32(xys, size);
+
+ let index = 0;
+ for (let j = 0; j < sortedFragments.length; j++) {
+ const fragment = sortedFragments[j];
+ const data: number[] = fragment.data;
+ const times_PMILLIS: number[] = fragment.times_PMILLIS;
+
+ for (let k = 0; k < data.length; k++ , index += 4) {
+ const x1 = timeAxis.vAtTime(times_PMILLIS[k]);
+ const y1 = data[k];
+
+ xys[index] = x1;
+ xys[index + 1] = baseline;
+
+ xys[index + 2] = x1;
+ xys[index + 3] = y1;
}
- else if ( timeseries.uiHint == 'bars' ) {
- var size = 8;
- xys = ensureCapacityFloat32( xys, size );
-
- var fragment = selection.hoveredTimeseries.fragment;
- var index = selection.hoveredTimeseries.index;
-
- if ( index < fragment.data.length )
- {
- var x1 = timeAxis.vAtTime( fragment.times_PMILLIS[index] );
- var y1 = fragment.data[index];
-
- var x2 = timeAxis.vAtTime( fragment.times_PMILLIS[index+1] );
- var y2 = fragment.data[index+1];
-
- xys[0] = x1; xys[1] = y1;
- xys[2] = x2; xys[3] = y1;
- xys[4] = x2; xys[5] = baseline;
- xys[6] = x1; xys[7] = baseline;
-
- var color = hasval( timeseries.lineColor ) ? timeseries.lineColor : defaultColor;
- u_Color.setData( gl, darker( color, 0.8 ) );
-
- xysBuffer.setData( xys.subarray( 0, size ) );
- a_Position.setDataAndEnable( gl, xysBuffer, 2, GL.FLOAT );
-
- gl.drawArrays( GL.LINE_LOOP, 0, size / 2 );
- }
+ }
+
+ const lineColor = hasval(timeseries.lineColor) ? timeseries.lineColor : defaultColor;
+ u_Color.setData(gl, lineColor);
+
+ xysBuffer.setData(xys.subarray(0, index));
+ a_Position.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+
+ gl.drawArrays(GL.TRIANGLE_STRIP, 0, size / 2);
+
+ program.endUse(gl);
+ }
+
+ // highlight hovered point
+ if (selection.hoveredTimeseries.fragment && timeseries.fragmentGuids.hasValue(selection.hoveredTimeseries.fragment.fragmentGuid)) {
+ // enable the shader
+ program.use(gl);
+ u_modelViewMatrix.setData(gl, glOrthoAxis(axis));
+
+ if (timeseries.uiHint === 'area' || timeseries.uiHint === 'lines' || timeseries.uiHint === 'points' || timeseries.uiHint === 'lines-and-points' || timeseries.uiHint === undefined) {
+ const size = 8;
+ xys = ensureCapacityFloat32(xys, size);
+
+ const vppx: number = timeAxis.vSize / viewport.w;
+ const vppy: number = dataAxis.vSize / viewport.h;
+
+ const highlightSize = hasval(timeseries.pointSize) ? timeseries.pointSize : 5;
+
+ const bufferx = (highlightSize / 2) * vppx;
+ const buffery = (highlightSize / 2) * vppy;
+
+ const fragment = selection.hoveredTimeseries.fragment;
+ const y = selection.hoveredTimeseries.data;
+ const x = timeAxis.vAtTime(selection.hoveredTimeseries.times_PMILLIS);
+
+ xys[0] = x - bufferx; xys[1] = y - buffery;
+ xys[2] = x + bufferx; xys[3] = y - buffery;
+ xys[4] = x + bufferx; xys[5] = y + buffery;
+ xys[6] = x - bufferx; xys[7] = y + buffery;
+
+ const color = hasval(timeseries.pointColor) ? timeseries.pointColor : defaultColor;
+ u_Color.setData(gl, darker(color, 0.8));
+
+ xysBuffer.setData(xys.subarray(0, size));
+ a_Position.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+
+ gl.drawArrays(GL.LINE_LOOP, 0, size / 2);
+ }
+ else if (timeseries.uiHint === 'bars') {
+ const size = 8;
+ xys = ensureCapacityFloat32(xys, size);
+
+ const fragment = selection.hoveredTimeseries.fragment;
+ const index = selection.hoveredTimeseries.index;
+
+ if (index < fragment.data.length) {
+ const x1 = timeAxis.vAtTime(fragment.times_PMILLIS[index]);
+ const y1 = fragment.data[index];
+
+ const x2 = timeAxis.vAtTime(fragment.times_PMILLIS[index + 1]);
+ const y2 = fragment.data[index + 1];
+
+ xys[0] = x1; xys[1] = y1;
+ xys[2] = x2; xys[3] = y1;
+ xys[4] = x2; xys[5] = baseline;
+ xys[6] = x1; xys[7] = baseline;
+
+ const color = hasval(timeseries.lineColor) ? timeseries.lineColor : defaultColor;
+ u_Color.setData(gl, darker(color, 0.8));
+
+ xysBuffer.setData(xys.subarray(0, size));
+ a_Position.setDataAndEnable(gl, xysBuffer, 2, GL.FLOAT);
+
+ gl.drawArrays(GL.LINE_LOOP, 0, size / 2);
}
}
+ program.endUse(gl);
}
-
- // disable shader and attribute buffers
- a_Position.disable( gl );
- program.endUse( gl );
}
- }
- }
+
+ // disable shader and attribute buffers
+ a_Position.disable(gl);
+ a_lpDistance.disable(gl);
+ };
+ };
}
+
diff --git a/src/webglimpse/timeline/timeline_ui.ts b/src/webglimpse/timeline/timeline_ui.ts
index 1e8851b..a0d1a88 100644
--- a/src/webglimpse/timeline/timeline_ui.ts
+++ b/src/webglimpse/timeline/timeline_ui.ts
@@ -27,491 +27,500 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-module Webglimpse {
+import { OrderedSet } from '../util/ordered_set';
+import { TimelineEventStyleUi } from './timeline_event_style';
+import { TimelineAnnotationStyleUi } from './timeline_annotation_style';
+import { SimpleModel, XyModel } from '../misc';
+import { StringMap, GL, hasval, getObjectId } from '../util/util';
+import { Texture2D } from '../texture';
+import { Notification, Notification1, Notification2 } from '../util/notification';
+import { Pane, PointerEvent, isLeftMouseDown } from '../core';
+import { TimelineModel, TimelineGroupModel, TimelineRowModel, TimelineEventModel, TimelineAnnotationModel, TimelineTimeseriesFragmentModel } from './timeline_model';
+import { TimelineRowPaneFactory } from './timeline_row';
+
+export interface TimelineUiOptions {
+ allowEventMultiSelection?: boolean;
+}
+
+export class TimelineUi {
+ private _input: TimelineInput;
+ private _selection: TimelineSelectionModel;
+
+ private _groupUis: OrderedSet;
+ private _rowUis: OrderedSet;
+ private _eventStyles: OrderedSet;
+ private _annotationStyles: OrderedSet;
+
+ private _millisPerPx: SimpleModel;
+
+ private _imageStatus: StringMap;
+ private _imageCache: StringMap;
+
+ private _dispose: Notification;
+
+ private _panes: OrderedSet;
+
+ constructor(model: TimelineModel, options: TimelineUiOptions = {}) {
+ this._dispose = new Notification();
+ this._input = new TimelineInput();
+
+ const getPaneId = function (pane: Pane): string {
+ const paneId = pane['webglimpse_PaneId'];
+ return hasval(paneId) ? paneId : getObjectId(pane);
+ };
+
+ this._panes = new OrderedSet([], getPaneId);
+
+ this._selection = new TimelineSelectionModel();
+ attachTimelineInputToSelection(this._input, this._selection, options);
+
+ this._groupUis = new OrderedSet([], (g) => g.groupGuid);
+ const groupUis = this._groupUis;
+ const addGroupUi = function (group: TimelineGroupModel) { groupUis.add(new TimelineGroupUi(group.groupGuid)); };
+ const removeGroupUi = function (group: TimelineGroupModel) { groupUis.removeId(group.groupGuid); };
+ model.groups.forEach(addGroupUi);
+ model.groups.valueAdded.on(addGroupUi);
+ model.groups.valueRemoved.on(removeGroupUi);
+
+ this._rowUis = new OrderedSet([], (r) => r.rowGuid);
+ const rowUis = this._rowUis;
+ const addRowUi = function (row: TimelineRowModel) { rowUis.add(new TimelineRowUi(row.rowGuid)); };
+ const removeRowUi = function (row: TimelineRowModel) { rowUis.removeId(row.rowGuid); };
+ model.rows.forEach(addRowUi);
+ model.rows.valueAdded.on(addRowUi);
+ model.rows.valueRemoved.on(removeRowUi);
+
+ this._eventStyles = new OrderedSet([], (s) => s.styleGuid);
+ this._annotationStyles = new OrderedSet([], (s) => s.styleGuid);
+
+ this._millisPerPx = new SimpleModel(1000);
+
+ this._imageStatus = {};
+ this._imageCache = {};
+
+ this._dispose.on(function () {
+ model.groups.valueAdded.off(addGroupUi);
+ model.groups.valueRemoved.off(removeGroupUi);
+ model.rows.valueAdded.off(addRowUi);
+ model.rows.valueRemoved.off(removeRowUi);
+ });
+ }
+ get input(): TimelineInput {
+ return this._input;
+ }
- export interface TimelineUiOptions {
- allowEventMultiSelection? : boolean;
+ get selection(): TimelineSelectionModel {
+ return this._selection;
}
-
- export class TimelineUi {
- private _input : TimelineInput;
- private _selection : TimelineSelectionModel;
- private _groupUis : OrderedSet;
- private _rowUis : OrderedSet;
- private _eventStyles : OrderedSet;
- private _annotationStyles : OrderedSet;
+ get groupUis(): OrderedSet {
+ return this._groupUis;
+ }
- private _millisPerPx : SimpleModel;
+ groupUi(groupGuid: string): TimelineGroupUi {
+ return this._groupUis.valueFor(groupGuid);
+ }
- private _imageStatus : StringMap